2246 lines
72 KiB
C++
2246 lines
72 KiB
C++
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
|
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
|
/* 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 "nsAppRunner.h"
|
|
#include "nsSystemInfo.h"
|
|
#include "prsystem.h"
|
|
#include "prio.h"
|
|
#include "mozilla/SSE.h"
|
|
#include "mozilla/arm.h"
|
|
#include "mozilla/dom/DOMMozPromiseRequestHolder.h"
|
|
#include "mozilla/Hal.h"
|
|
#include "mozilla/LazyIdleThread.h"
|
|
#include "mozilla/LookAndFeel.h"
|
|
#include "mozilla/Sprintf.h"
|
|
#include "mozilla/Try.h"
|
|
#include "mozilla/Vector.h"
|
|
#include "jsapi.h"
|
|
#include "js/PropertyAndElement.h" // JS_SetProperty
|
|
#include "mozilla/dom/Promise.h"
|
|
#include "mozilla/glean/XpcomMetrics.h"
|
|
|
|
#ifdef XP_WIN
|
|
# include <comutil.h>
|
|
# include <time.h>
|
|
# ifndef __MINGW32__
|
|
# include <iwscapi.h>
|
|
# endif // __MINGW32__
|
|
# include <windows.h>
|
|
# include <winioctl.h>
|
|
# ifndef __MINGW32__
|
|
# include <wrl.h>
|
|
# include <wscapi.h>
|
|
# endif // __MINGW32__
|
|
# include "base/scoped_handle_win.h"
|
|
# include "mozilla/DynamicallyLinkedFunctionPtr.h"
|
|
# include "mozilla/WindowsVersion.h"
|
|
# include "nsAppDirectoryServiceDefs.h"
|
|
# include "nsDirectoryServiceDefs.h"
|
|
# include "nsDirectoryServiceUtils.h"
|
|
# include "nsWindowsHelpers.h"
|
|
# include "nsIWindowsRegKey.h"
|
|
# include "WinUtils.h"
|
|
# include "mozilla/NotNull.h"
|
|
|
|
#endif
|
|
|
|
#ifdef XP_MACOSX
|
|
# include "MacHelpers.h"
|
|
#endif
|
|
|
|
#ifdef MOZ_WIDGET_GTK
|
|
# include <gtk/gtk.h>
|
|
# include <dlfcn.h>
|
|
# include "mozilla/WidgetUtilsGtk.h"
|
|
#endif
|
|
|
|
#if defined(XP_LINUX)
|
|
# include <unistd.h>
|
|
# include <fstream>
|
|
# include "mozilla/Tokenizer.h"
|
|
# include "mozilla/widget/LSBUtils.h"
|
|
# include "nsCharSeparatedTokenizer.h"
|
|
|
|
# include <map>
|
|
# include <string>
|
|
#endif
|
|
|
|
#ifdef MOZ_WIDGET_ANDROID
|
|
# include "AndroidBuild.h"
|
|
# include "mozilla/java/GeckoAppShellWrappers.h"
|
|
# include "mozilla/jni/Utils.h"
|
|
#endif
|
|
|
|
#ifdef XP_MACOSX
|
|
# include <sys/sysctl.h>
|
|
#endif
|
|
|
|
#if defined(XP_LINUX) && defined(MOZ_SANDBOX)
|
|
# include "mozilla/SandboxInfo.h"
|
|
#endif
|
|
|
|
// Slot for NS_InitXPCOM to pass information to nsSystemInfo::Init.
|
|
// Only set to nonzero (potentially) if XP_UNIX. On such systems, the
|
|
// system call to discover the appropriate value is not thread-safe,
|
|
// so we must call it before going multithreaded, but nsSystemInfo::Init
|
|
// only happens well after that point.
|
|
uint32_t nsSystemInfo::gUserUmask = 0;
|
|
|
|
using namespace mozilla::dom;
|
|
|
|
#if defined(XP_WIN)
|
|
# define RuntimeClass_Windows_System_Profile_WindowsIntegrityPolicy \
|
|
L"Windows.System.Profile.WindowsIntegrityPolicy"
|
|
# ifndef __MINGW32__
|
|
using namespace Microsoft::WRL;
|
|
using namespace Microsoft::WRL::Wrappers;
|
|
using namespace ABI::Windows::Foundation;
|
|
# endif // __MINGW32__
|
|
#endif
|
|
|
|
#if defined(XP_LINUX)
|
|
static void SimpleParseKeyValuePairs(
|
|
const std::string& aFilename,
|
|
std::map<nsCString, nsCString>& aKeyValuePairs) {
|
|
std::ifstream input(aFilename.c_str());
|
|
if (!input.is_open()) {
|
|
return;
|
|
}
|
|
for (std::string line; std::getline(input, line);) {
|
|
nsAutoCString key, value;
|
|
|
|
nsCCharSeparatedTokenizer tokens(nsDependentCString(line.c_str()), ':');
|
|
if (tokens.hasMoreTokens()) {
|
|
key = tokens.nextToken();
|
|
if (tokens.hasMoreTokens()) {
|
|
value = tokens.nextToken();
|
|
}
|
|
// We want the value even if there was just one token, to cover the
|
|
// case where we had the key, and the value was blank (seems to be
|
|
// a valid scenario some files.)
|
|
aKeyValuePairs[key] = value;
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
#ifdef XP_WIN
|
|
// Lifted from media/webrtc/trunk/webrtc/base/systeminfo.cc,
|
|
// so keeping the _ instead of switching to camel case for now.
|
|
static void GetProcessorInformation(int* physical_cpus, int* cache_size_L2,
|
|
int* cache_size_L3) {
|
|
MOZ_ASSERT(physical_cpus && cache_size_L2 && cache_size_L3);
|
|
|
|
*physical_cpus = 0;
|
|
*cache_size_L2 = 0; // This will be in kbytes
|
|
*cache_size_L3 = 0; // This will be in kbytes
|
|
|
|
// Determine buffer size, allocate and get processor information.
|
|
// Size can change between calls (unlikely), so a loop is done.
|
|
SYSTEM_LOGICAL_PROCESSOR_INFORMATION info_buffer[32];
|
|
SYSTEM_LOGICAL_PROCESSOR_INFORMATION* infos = &info_buffer[0];
|
|
DWORD return_length = sizeof(info_buffer);
|
|
while (!::GetLogicalProcessorInformation(infos, &return_length)) {
|
|
if (GetLastError() == ERROR_INSUFFICIENT_BUFFER &&
|
|
infos == &info_buffer[0]) {
|
|
infos = new SYSTEM_LOGICAL_PROCESSOR_INFORMATION
|
|
[return_length / sizeof(SYSTEM_LOGICAL_PROCESSOR_INFORMATION)];
|
|
} else {
|
|
return;
|
|
}
|
|
}
|
|
|
|
for (size_t i = 0;
|
|
i < return_length / sizeof(SYSTEM_LOGICAL_PROCESSOR_INFORMATION); ++i) {
|
|
if (infos[i].Relationship == RelationProcessorCore) {
|
|
++*physical_cpus;
|
|
} else if (infos[i].Relationship == RelationCache) {
|
|
// Only care about L2 and L3 cache
|
|
switch (infos[i].Cache.Level) {
|
|
case 2:
|
|
*cache_size_L2 = static_cast<int>(infos[i].Cache.Size / 1024);
|
|
break;
|
|
case 3:
|
|
*cache_size_L3 = static_cast<int>(infos[i].Cache.Size / 1024);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (infos != &info_buffer[0]) {
|
|
delete[] infos;
|
|
}
|
|
return;
|
|
}
|
|
#endif
|
|
|
|
#if defined(XP_WIN)
|
|
namespace {
|
|
static nsresult GetFolderDiskInfo(nsIFile* file, FolderDiskInfo& info) {
|
|
info.model.Truncate();
|
|
info.revision.Truncate();
|
|
info.isSSD = false;
|
|
|
|
nsAutoString filePath;
|
|
nsresult rv = file->GetPath(filePath);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
wchar_t volumeMountPoint[MAX_PATH] = {L'\\', L'\\', L'.', L'\\'};
|
|
const size_t PREFIX_LEN = 4;
|
|
if (!::GetVolumePathNameW(filePath.get(), volumeMountPoint + PREFIX_LEN,
|
|
std::size(volumeMountPoint) - PREFIX_LEN)) {
|
|
return NS_ERROR_UNEXPECTED;
|
|
}
|
|
size_t volumeMountPointLen = wcslen(volumeMountPoint);
|
|
// Since we would like to open a drive and not a directory, we need to
|
|
// remove any trailing backslash. A drive handle is valid for
|
|
// DeviceIoControl calls, a directory handle is not.
|
|
if (volumeMountPoint[volumeMountPointLen - 1] == L'\\') {
|
|
volumeMountPoint[volumeMountPointLen - 1] = L'\0';
|
|
}
|
|
ScopedHandle handle(::CreateFileW(volumeMountPoint, 0,
|
|
FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr,
|
|
OPEN_EXISTING, 0, nullptr));
|
|
if (!handle.IsValid()) {
|
|
return NS_ERROR_UNEXPECTED;
|
|
}
|
|
STORAGE_PROPERTY_QUERY queryParameters = {StorageDeviceProperty,
|
|
PropertyStandardQuery};
|
|
STORAGE_DEVICE_DESCRIPTOR outputHeader = {sizeof(STORAGE_DEVICE_DESCRIPTOR)};
|
|
DWORD bytesRead = 0;
|
|
if (!::DeviceIoControl(handle, IOCTL_STORAGE_QUERY_PROPERTY, &queryParameters,
|
|
sizeof(queryParameters), &outputHeader,
|
|
sizeof(outputHeader), &bytesRead, nullptr)) {
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
PSTORAGE_DEVICE_DESCRIPTOR deviceOutput =
|
|
(PSTORAGE_DEVICE_DESCRIPTOR)malloc(outputHeader.Size);
|
|
if (!::DeviceIoControl(handle, IOCTL_STORAGE_QUERY_PROPERTY, &queryParameters,
|
|
sizeof(queryParameters), deviceOutput,
|
|
outputHeader.Size, &bytesRead, nullptr)) {
|
|
free(deviceOutput);
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
|
|
queryParameters.PropertyId = StorageDeviceTrimProperty;
|
|
bytesRead = 0;
|
|
bool isSSD = false;
|
|
DEVICE_TRIM_DESCRIPTOR trimDescriptor = {sizeof(DEVICE_TRIM_DESCRIPTOR)};
|
|
if (::DeviceIoControl(handle, IOCTL_STORAGE_QUERY_PROPERTY, &queryParameters,
|
|
sizeof(queryParameters), &trimDescriptor,
|
|
sizeof(trimDescriptor), &bytesRead, nullptr)) {
|
|
if (trimDescriptor.TrimEnabled) {
|
|
isSSD = true;
|
|
}
|
|
}
|
|
|
|
if (isSSD) {
|
|
// Get Seek Penalty
|
|
queryParameters.PropertyId = StorageDeviceSeekPenaltyProperty;
|
|
bytesRead = 0;
|
|
DEVICE_SEEK_PENALTY_DESCRIPTOR seekPenaltyDescriptor = {
|
|
sizeof(DEVICE_SEEK_PENALTY_DESCRIPTOR)};
|
|
if (::DeviceIoControl(handle, IOCTL_STORAGE_QUERY_PROPERTY,
|
|
&queryParameters, sizeof(queryParameters),
|
|
&seekPenaltyDescriptor, sizeof(seekPenaltyDescriptor),
|
|
&bytesRead, nullptr)) {
|
|
// It is possible that the disk has TrimEnabled, but also
|
|
// IncursSeekPenalty; In this case, this is an HDD
|
|
if (seekPenaltyDescriptor.IncursSeekPenalty) {
|
|
isSSD = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Some HDDs are including product ID info in the vendor field. Since PNP
|
|
// IDs include vendor info and product ID concatenated together, we'll do
|
|
// that here and interpret the result as a unique ID for the HDD model.
|
|
if (deviceOutput->VendorIdOffset) {
|
|
info.model =
|
|
reinterpret_cast<char*>(deviceOutput) + deviceOutput->VendorIdOffset;
|
|
}
|
|
if (deviceOutput->ProductIdOffset) {
|
|
info.model +=
|
|
reinterpret_cast<char*>(deviceOutput) + deviceOutput->ProductIdOffset;
|
|
}
|
|
info.model.CompressWhitespace();
|
|
if (deviceOutput->ProductRevisionOffset) {
|
|
info.revision = reinterpret_cast<char*>(deviceOutput) +
|
|
deviceOutput->ProductRevisionOffset;
|
|
info.revision.CompressWhitespace();
|
|
}
|
|
info.isSSD = isSSD;
|
|
free(deviceOutput);
|
|
return NS_OK;
|
|
}
|
|
|
|
static nsresult CollectDiskInfo(nsIFile* greDir, nsIFile* winDir,
|
|
nsIFile* profDir, DiskInfo& info) {
|
|
nsresult rv = GetFolderDiskInfo(greDir, info.binary);
|
|
if (NS_FAILED(rv)) {
|
|
return rv;
|
|
}
|
|
rv = GetFolderDiskInfo(winDir, info.system);
|
|
if (NS_FAILED(rv)) {
|
|
return rv;
|
|
}
|
|
return GetFolderDiskInfo(profDir, info.profile);
|
|
}
|
|
|
|
static nsresult CollectOSInfo(OSInfo& info) {
|
|
HKEY installYearHKey;
|
|
LONG status = RegOpenKeyExW(
|
|
HKEY_LOCAL_MACHINE, L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion", 0,
|
|
KEY_READ | KEY_WOW64_64KEY, &installYearHKey);
|
|
|
|
if (status != ERROR_SUCCESS) {
|
|
return NS_ERROR_UNEXPECTED;
|
|
}
|
|
|
|
nsAutoRegKey installYearKey(installYearHKey);
|
|
|
|
DWORD type = 0;
|
|
time_t raw_time = 0;
|
|
DWORD time_size = sizeof(time_t);
|
|
|
|
status = RegQueryValueExW(installYearHKey, L"InstallDate", nullptr, &type,
|
|
(LPBYTE)&raw_time, &time_size);
|
|
|
|
if (status != ERROR_SUCCESS) {
|
|
return NS_ERROR_UNEXPECTED;
|
|
}
|
|
|
|
if (type != REG_DWORD) {
|
|
return NS_ERROR_UNEXPECTED;
|
|
}
|
|
|
|
tm time;
|
|
if (localtime_s(&time, &raw_time) != 0) {
|
|
return NS_ERROR_UNEXPECTED;
|
|
}
|
|
|
|
info.installYear = 1900UL + time.tm_year;
|
|
|
|
nsAutoServiceHandle scm(
|
|
OpenSCManager(nullptr, SERVICES_ACTIVE_DATABASE, SC_MANAGER_CONNECT));
|
|
|
|
if (!scm) {
|
|
return NS_ERROR_UNEXPECTED;
|
|
}
|
|
|
|
bool superfetchServiceRunning = false;
|
|
|
|
// Superfetch was introduced in Windows Vista as a service with the name
|
|
// SysMain. The service display name was also renamed to SysMain after Windows
|
|
// 10 build 1809.
|
|
nsAutoServiceHandle hService(OpenService(scm, L"SysMain", GENERIC_READ));
|
|
|
|
if (hService) {
|
|
SERVICE_STATUS superfetchStatus;
|
|
LPSERVICE_STATUS pSuperfetchStatus = &superfetchStatus;
|
|
|
|
if (!QueryServiceStatus(hService, pSuperfetchStatus)) {
|
|
return NS_ERROR_UNEXPECTED;
|
|
}
|
|
|
|
superfetchServiceRunning =
|
|
superfetchStatus.dwCurrentState == SERVICE_RUNNING;
|
|
}
|
|
|
|
// If the SysMain (Superfetch) service is available, but not configured using
|
|
// the defaults, then it's disabled for our purposes, since it's not going to
|
|
// be operating as expected.
|
|
bool superfetchUsingDefaultParams = true;
|
|
bool prefetchUsingDefaultParams = true;
|
|
|
|
static const WCHAR prefetchParamsKeyName[] =
|
|
L"SYSTEM\\CurrentControlSet\\Control\\Session Manager\\Memory "
|
|
L"Management\\PrefetchParameters";
|
|
static const DWORD SUPERFETCH_DEFAULT_PARAM = 3;
|
|
static const DWORD PREFETCH_DEFAULT_PARAM = 3;
|
|
|
|
HKEY prefetchParamsHKey;
|
|
|
|
LONG prefetchParamsStatus =
|
|
RegOpenKeyExW(HKEY_LOCAL_MACHINE, prefetchParamsKeyName, 0,
|
|
KEY_READ | KEY_WOW64_64KEY, &prefetchParamsHKey);
|
|
|
|
if (prefetchParamsStatus == ERROR_SUCCESS) {
|
|
DWORD valueSize = sizeof(DWORD);
|
|
DWORD superfetchValue = 0;
|
|
nsAutoRegKey prefetchParamsKey(prefetchParamsHKey);
|
|
LONG superfetchParamStatus = RegQueryValueExW(
|
|
prefetchParamsHKey, L"EnableSuperfetch", nullptr, &type,
|
|
reinterpret_cast<LPBYTE>(&superfetchValue), &valueSize);
|
|
|
|
// If the EnableSuperfetch registry key doesn't exist, then it's using the
|
|
// default configuration.
|
|
if (superfetchParamStatus == ERROR_SUCCESS &&
|
|
superfetchValue != SUPERFETCH_DEFAULT_PARAM) {
|
|
superfetchUsingDefaultParams = false;
|
|
}
|
|
|
|
DWORD prefetchValue = 0;
|
|
|
|
LONG prefetchParamStatus = RegQueryValueExW(
|
|
prefetchParamsHKey, L"EnablePrefetcher", nullptr, &type,
|
|
reinterpret_cast<LPBYTE>(&prefetchValue), &valueSize);
|
|
|
|
// If the EnablePrefetcher registry key doesn't exist, then we interpret
|
|
// that as the Prefetcher being disabled (since Prefetch behaviour when
|
|
// the key is not available appears to be undefined).
|
|
if (prefetchParamStatus != ERROR_SUCCESS ||
|
|
prefetchValue != PREFETCH_DEFAULT_PARAM) {
|
|
prefetchUsingDefaultParams = false;
|
|
}
|
|
}
|
|
|
|
info.hasSuperfetch = superfetchServiceRunning && superfetchUsingDefaultParams;
|
|
info.hasPrefetch = prefetchUsingDefaultParams;
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
nsresult CollectCountryCode(nsAString& aCountryCode) {
|
|
GEOID geoid = GetUserGeoID(GEOCLASS_NATION);
|
|
if (geoid == GEOID_NOT_AVAILABLE) {
|
|
return NS_ERROR_NOT_AVAILABLE;
|
|
}
|
|
// Get required length
|
|
int numChars = GetGeoInfoW(geoid, GEO_ISO2, nullptr, 0, 0);
|
|
if (!numChars) {
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
// Now get the string for real
|
|
aCountryCode.SetLength(numChars);
|
|
numChars =
|
|
GetGeoInfoW(geoid, GEO_ISO2, char16ptr_t(aCountryCode.BeginWriting()),
|
|
aCountryCode.Length(), 0);
|
|
if (!numChars) {
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
|
|
// numChars includes null terminator
|
|
aCountryCode.Truncate(numChars - 1);
|
|
return NS_OK;
|
|
}
|
|
|
|
} // namespace
|
|
|
|
# ifndef __MINGW32__
|
|
|
|
static HRESULT EnumWSCProductList(
|
|
nsAString& aOutput, mozilla::NotNull<IWSCProductList*> aProdList) {
|
|
MOZ_ASSERT(aOutput.IsEmpty());
|
|
|
|
LONG count;
|
|
HRESULT hr = aProdList->get_Count(&count);
|
|
if (FAILED(hr)) {
|
|
return hr;
|
|
}
|
|
|
|
for (LONG index = 0; index < count; ++index) {
|
|
RefPtr<IWscProduct> product;
|
|
hr = aProdList->get_Item(index, getter_AddRefs(product));
|
|
if (FAILED(hr)) {
|
|
return hr;
|
|
}
|
|
|
|
WSC_SECURITY_PRODUCT_STATE state;
|
|
hr = product->get_ProductState(&state);
|
|
if (FAILED(hr)) {
|
|
return hr;
|
|
}
|
|
|
|
// We only care about products that are active
|
|
if (state == WSC_SECURITY_PRODUCT_STATE_OFF ||
|
|
state == WSC_SECURITY_PRODUCT_STATE_SNOOZED) {
|
|
continue;
|
|
}
|
|
|
|
_bstr_t bName;
|
|
hr = product->get_ProductName(bName.GetAddress());
|
|
if (FAILED(hr)) {
|
|
return hr;
|
|
}
|
|
|
|
if (!aOutput.IsEmpty()) {
|
|
aOutput.AppendLiteral(u";");
|
|
}
|
|
|
|
aOutput.Append((wchar_t*)bName, bName.length());
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
static nsresult GetWindowsSecurityCenterInfo(nsAString& aAVInfo,
|
|
nsAString& aAntiSpyInfo,
|
|
nsAString& aFirewallInfo) {
|
|
aAVInfo.Truncate();
|
|
aAntiSpyInfo.Truncate();
|
|
aFirewallInfo.Truncate();
|
|
|
|
if (!XRE_IsParentProcess()) {
|
|
return NS_ERROR_NOT_AVAILABLE;
|
|
}
|
|
|
|
const CLSID clsid = __uuidof(WSCProductList);
|
|
const IID iid = __uuidof(IWSCProductList);
|
|
|
|
// NB: A separate instance of IWSCProductList is needed for each distinct
|
|
// security provider type; MSDN says that we cannot reuse the same object
|
|
// and call Initialize() to pave over the previous data.
|
|
|
|
WSC_SECURITY_PROVIDER providerTypes[] = {WSC_SECURITY_PROVIDER_ANTIVIRUS,
|
|
WSC_SECURITY_PROVIDER_ANTISPYWARE,
|
|
WSC_SECURITY_PROVIDER_FIREWALL};
|
|
|
|
// Each output must match the corresponding entry in providerTypes.
|
|
nsAString* outputs[] = {&aAVInfo, &aAntiSpyInfo, &aFirewallInfo};
|
|
|
|
static_assert(std::size(providerTypes) == std::size(outputs),
|
|
"Length of providerTypes and outputs arrays must match");
|
|
|
|
for (uint32_t index = 0; index < std::size(providerTypes); ++index) {
|
|
RefPtr<IWSCProductList> prodList;
|
|
HRESULT hr = ::CoCreateInstance(clsid, nullptr, CLSCTX_INPROC_SERVER, iid,
|
|
getter_AddRefs(prodList));
|
|
if (FAILED(hr)) {
|
|
return NS_ERROR_NOT_AVAILABLE;
|
|
}
|
|
|
|
hr = prodList->Initialize(providerTypes[index]);
|
|
if (FAILED(hr)) {
|
|
return NS_ERROR_UNEXPECTED;
|
|
}
|
|
|
|
hr = EnumWSCProductList(*outputs[index],
|
|
mozilla::WrapNotNull(prodList.get()));
|
|
if (FAILED(hr)) {
|
|
return NS_ERROR_UNEXPECTED;
|
|
}
|
|
}
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
# endif // __MINGW32__
|
|
|
|
#endif // defined(XP_WIN)
|
|
|
|
#ifdef XP_MACOSX
|
|
static nsresult GetAppleModelId(nsAutoCString& aModelId) {
|
|
size_t numChars = 0;
|
|
size_t result = sysctlbyname("hw.model", nullptr, &numChars, nullptr, 0);
|
|
if (result != 0 || !numChars) {
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
aModelId.SetLength(numChars);
|
|
result =
|
|
sysctlbyname("hw.model", aModelId.BeginWriting(), &numChars, nullptr, 0);
|
|
if (result != 0) {
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
// numChars includes null terminator
|
|
aModelId.Truncate(numChars - 1);
|
|
return NS_OK;
|
|
}
|
|
|
|
static nsresult ProcessIsRosettaTranslated(bool& isRosetta) {
|
|
# if defined(__aarch64__)
|
|
// There is no need to call sysctlbyname() if we are running as arm64.
|
|
isRosetta = false;
|
|
# else
|
|
int ret = 0;
|
|
size_t size = sizeof(ret);
|
|
if (sysctlbyname("sysctl.proc_translated", &ret, &size, NULL, 0) == -1) {
|
|
if (errno != ENOENT) {
|
|
fprintf(stderr, "Failed to check for translation environment\n");
|
|
}
|
|
isRosetta = false;
|
|
} else {
|
|
isRosetta = (ret == 1);
|
|
}
|
|
# endif
|
|
return NS_OK;
|
|
}
|
|
#endif
|
|
|
|
#ifdef XP_WIN
|
|
static nsresult GetWinModelId(nsAutoString& aModelId) {
|
|
nsCOMPtr<nsIWindowsRegKey> regKey =
|
|
do_GetService("@mozilla.org/windows-registry-key;1");
|
|
NS_ENSURE_TRUE(regKey, NS_ERROR_FAILURE);
|
|
const nsString regPath(
|
|
u"SYSTEM\\CurrentControlSet\\Control\\SystemInformation");
|
|
nsresult rv = regKey->Open(nsIWindowsRegKey::ROOT_KEY_LOCAL_MACHINE, regPath,
|
|
nsIWindowsRegKey::ACCESS_READ);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
auto defer = mozilla::MakeScopeExit([&] { regKey->Close(); });
|
|
return regKey->ReadStringValue(u"SystemProductName"_ns, aModelId);
|
|
}
|
|
#endif
|
|
|
|
#if defined(XP_LINUX)
|
|
static nsresult GetLinuxProductName(nsAutoCString& aProductName) {
|
|
std::ifstream input("/sys/devices/virtual/dmi/id/product_name");
|
|
if (!input.is_open()) {
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
std::string line;
|
|
if (!std::getline(input, line)) {
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
aProductName = line.c_str();
|
|
return NS_OK;
|
|
}
|
|
|
|
static nsresult GetLinuxProductSku(nsAutoCString& aProductSku) {
|
|
std::ifstream input("/sys/devices/virtual/dmi/id/product_sku");
|
|
if (!input.is_open()) {
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
std::string line;
|
|
if (!std::getline(input, line)) {
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
aProductSku = line.c_str();
|
|
return NS_OK;
|
|
}
|
|
#endif
|
|
|
|
using namespace mozilla;
|
|
|
|
nsSystemInfo::nsSystemInfo() = default;
|
|
|
|
nsSystemInfo::~nsSystemInfo() = default;
|
|
|
|
// CPU-specific information.
|
|
static const struct PropItems {
|
|
const char* name;
|
|
bool (*propfun)(void);
|
|
} cpuPropItems[] = {
|
|
// x86-specific bits.
|
|
{"hasMMX", mozilla::supports_mmx},
|
|
{"hasSSE", mozilla::supports_sse},
|
|
{"hasSSE2", mozilla::supports_sse2},
|
|
{"hasSSE3", mozilla::supports_sse3},
|
|
{"hasSSSE3", mozilla::supports_ssse3},
|
|
{"hasSSE4A", mozilla::supports_sse4a},
|
|
{"hasSSE4_1", mozilla::supports_sse4_1},
|
|
{"hasSSE4_2", mozilla::supports_sse4_2},
|
|
{"hasAVX", mozilla::supports_avx},
|
|
{"hasAVX2", mozilla::supports_avx2},
|
|
{"hasAES", mozilla::supports_aes},
|
|
// ARM-specific bits.
|
|
{"hasEDSP", mozilla::supports_edsp},
|
|
{"hasARMv6", mozilla::supports_armv6},
|
|
{"hasARMv7", mozilla::supports_armv7},
|
|
{"hasNEON", mozilla::supports_neon}};
|
|
|
|
nsresult CollectProcessInfo(ProcessInfo& info) {
|
|
nsAutoCString cpuVendor;
|
|
nsAutoCString cpuName;
|
|
int cpuSpeed = -1;
|
|
int cpuFamily = -1;
|
|
int cpuModel = -1;
|
|
int cpuStepping = -1;
|
|
int logicalCPUs = -1;
|
|
int physicalCPUs = -1;
|
|
int cacheSizeL2 = -1;
|
|
int cacheSizeL3 = -1;
|
|
|
|
#if defined(XP_WIN)
|
|
// IsWow64Process2 is only available on Windows 10+, so we have to dynamically
|
|
// check for its existence.
|
|
typedef BOOL(WINAPI * LPFN_IWP2)(HANDLE, USHORT*, USHORT*);
|
|
LPFN_IWP2 iwp2 = reinterpret_cast<LPFN_IWP2>(
|
|
GetProcAddress(GetModuleHandle(L"kernel32"), "IsWow64Process2"));
|
|
BOOL isWow64 = FALSE;
|
|
USHORT processMachine = IMAGE_FILE_MACHINE_UNKNOWN;
|
|
USHORT nativeMachine = IMAGE_FILE_MACHINE_UNKNOWN;
|
|
BOOL gotWow64Value;
|
|
if (iwp2) {
|
|
gotWow64Value = iwp2(GetCurrentProcess(), &processMachine, &nativeMachine);
|
|
if (gotWow64Value) {
|
|
isWow64 = (processMachine != IMAGE_FILE_MACHINE_UNKNOWN);
|
|
}
|
|
} else {
|
|
gotWow64Value = IsWow64Process(GetCurrentProcess(), &isWow64);
|
|
// The function only indicates a WOW64 environment if it's 32-bit x86
|
|
// running on x86-64, so emulate what IsWow64Process2 would have given.
|
|
if (gotWow64Value && isWow64) {
|
|
processMachine = IMAGE_FILE_MACHINE_I386;
|
|
nativeMachine = IMAGE_FILE_MACHINE_AMD64;
|
|
}
|
|
}
|
|
NS_WARNING_ASSERTION(gotWow64Value, "IsWow64Process failed");
|
|
if (gotWow64Value) {
|
|
// Set this always, even for the x86-on-arm64 case.
|
|
info.isWow64 = !!isWow64;
|
|
// Additional information if we're running x86-on-arm64
|
|
info.isWowARM64 = (processMachine == IMAGE_FILE_MACHINE_I386 &&
|
|
nativeMachine == IMAGE_FILE_MACHINE_ARM64);
|
|
}
|
|
|
|
// S Mode
|
|
|
|
# ifndef __MINGW32__
|
|
// WindowsIntegrityPolicy is only available on newer versions
|
|
// of Windows 10, so there's no point in trying to check this
|
|
// on earlier versions. We know GetActivationFactory crashes on
|
|
// Windows 7 when trying to retrieve this class, and may also
|
|
// crash on very old versions of Windows 10.
|
|
if (IsWin10Sep2018UpdateOrLater()) {
|
|
ComPtr<IWindowsIntegrityPolicyStatics> wip;
|
|
HRESULT hr = GetActivationFactory(
|
|
HStringReference(
|
|
RuntimeClass_Windows_System_Profile_WindowsIntegrityPolicy)
|
|
.Get(),
|
|
&wip);
|
|
if (SUCCEEDED(hr)) {
|
|
// info.isWindowsSMode ends up true if Windows is in S mode, otherwise
|
|
// false
|
|
// https://docs.microsoft.com/en-us/uwp/api/windows.system.profile.windowsintegritypolicy.isenabled?view=winrt-22000
|
|
hr = wip->get_IsEnabled(&info.isWindowsSMode);
|
|
NS_WARNING_ASSERTION(SUCCEEDED(hr),
|
|
"WindowsIntegrityPolicy.IsEnabled failed");
|
|
}
|
|
}
|
|
# endif // __MINGW32__
|
|
|
|
// CPU speed
|
|
HKEY key;
|
|
static const WCHAR keyName[] =
|
|
L"HARDWARE\\DESCRIPTION\\System\\CentralProcessor\\0";
|
|
|
|
if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, keyName, 0, KEY_QUERY_VALUE, &key) ==
|
|
ERROR_SUCCESS) {
|
|
DWORD data, len, vtype;
|
|
len = sizeof(data);
|
|
|
|
if (RegQueryValueEx(key, L"~Mhz", 0, 0, reinterpret_cast<LPBYTE>(&data),
|
|
&len) == ERROR_SUCCESS) {
|
|
cpuSpeed = static_cast<int>(data);
|
|
}
|
|
|
|
// Limit to 64 double byte characters, should be plenty, but create
|
|
// a buffer one larger as the result may not be null terminated. If
|
|
// it is more than 64, we will not get the value.
|
|
wchar_t cpuVendorStr[64 + 1];
|
|
len = sizeof(cpuVendorStr) - 2;
|
|
if (RegQueryValueExW(key, L"VendorIdentifier", 0, &vtype,
|
|
reinterpret_cast<LPBYTE>(cpuVendorStr),
|
|
&len) == ERROR_SUCCESS &&
|
|
vtype == REG_SZ && len % 2 == 0 && len > 1) {
|
|
cpuVendorStr[len / 2] = 0; // In case it isn't null terminated
|
|
CopyUTF16toUTF8(nsDependentString(cpuVendorStr), cpuVendor);
|
|
}
|
|
|
|
// Limit to 64 double byte characters, should be plenty, but create
|
|
// a buffer one larger as the result may not be null terminated. If
|
|
// it is more than 64, we will not get the value.
|
|
// The expected string size is 48 characters or less.
|
|
wchar_t cpuNameStr[64 + 1];
|
|
len = sizeof(cpuNameStr) - 2;
|
|
if (RegQueryValueExW(key, L"ProcessorNameString", 0, &vtype,
|
|
reinterpret_cast<LPBYTE>(cpuNameStr),
|
|
&len) == ERROR_SUCCESS &&
|
|
vtype == REG_SZ && len % 2 == 0 && len > 1) {
|
|
cpuNameStr[len / 2] = 0; // In case it isn't null terminated
|
|
CopyUTF16toUTF8(nsDependentString(cpuNameStr), cpuName);
|
|
}
|
|
|
|
RegCloseKey(key);
|
|
}
|
|
|
|
// Other CPU attributes:
|
|
SYSTEM_INFO si;
|
|
GetNativeSystemInfo(&si);
|
|
logicalCPUs = si.dwNumberOfProcessors;
|
|
GetProcessorInformation(&physicalCPUs, &cacheSizeL2, &cacheSizeL3);
|
|
if (physicalCPUs <= 0) {
|
|
physicalCPUs = logicalCPUs;
|
|
}
|
|
cpuFamily = si.wProcessorLevel;
|
|
cpuModel = si.wProcessorRevision >> 8;
|
|
cpuStepping = si.wProcessorRevision & 0xFF;
|
|
#elif defined(XP_MACOSX)
|
|
// CPU speed
|
|
uint64_t sysctlValue64 = 0;
|
|
uint32_t sysctlValue32 = 0;
|
|
size_t len = 0;
|
|
len = sizeof(sysctlValue64);
|
|
if (!sysctlbyname("hw.cpufrequency_max", &sysctlValue64, &len, NULL, 0)) {
|
|
cpuSpeed = static_cast<int>(sysctlValue64 / 1000000);
|
|
}
|
|
MOZ_ASSERT(sizeof(sysctlValue64) == len);
|
|
|
|
len = sizeof(sysctlValue32);
|
|
if (!sysctlbyname("hw.physicalcpu_max", &sysctlValue32, &len, NULL, 0)) {
|
|
physicalCPUs = static_cast<int>(sysctlValue32);
|
|
}
|
|
MOZ_ASSERT(sizeof(sysctlValue32) == len);
|
|
|
|
len = sizeof(sysctlValue32);
|
|
if (!sysctlbyname("hw.logicalcpu_max", &sysctlValue32, &len, NULL, 0)) {
|
|
logicalCPUs = static_cast<int>(sysctlValue32);
|
|
}
|
|
MOZ_ASSERT(sizeof(sysctlValue32) == len);
|
|
|
|
len = sizeof(sysctlValue64);
|
|
if (!sysctlbyname("hw.l2cachesize", &sysctlValue64, &len, NULL, 0)) {
|
|
cacheSizeL2 = static_cast<int>(sysctlValue64 / 1024);
|
|
}
|
|
MOZ_ASSERT(sizeof(sysctlValue64) == len);
|
|
|
|
len = sizeof(sysctlValue64);
|
|
if (!sysctlbyname("hw.l3cachesize", &sysctlValue64, &len, NULL, 0)) {
|
|
cacheSizeL3 = static_cast<int>(sysctlValue64 / 1024);
|
|
}
|
|
MOZ_ASSERT(sizeof(sysctlValue64) == len);
|
|
|
|
if (!sysctlbyname("machdep.cpu.vendor", NULL, &len, NULL, 0)) {
|
|
char* cpuVendorStr = new char[len];
|
|
if (!sysctlbyname("machdep.cpu.vendor", cpuVendorStr, &len, NULL, 0)) {
|
|
cpuVendor = cpuVendorStr;
|
|
}
|
|
delete[] cpuVendorStr;
|
|
}
|
|
|
|
if (!sysctlbyname("machdep.cpu.brand_string", NULL, &len, NULL, 0)) {
|
|
char* cpuNameStr = new char[len];
|
|
if (!sysctlbyname("machdep.cpu.brand_string", cpuNameStr, &len, NULL, 0)) {
|
|
cpuName = cpuNameStr;
|
|
}
|
|
delete[] cpuNameStr;
|
|
}
|
|
|
|
len = sizeof(sysctlValue32);
|
|
if (!sysctlbyname("machdep.cpu.family", &sysctlValue32, &len, NULL, 0)) {
|
|
cpuFamily = static_cast<int>(sysctlValue32);
|
|
}
|
|
MOZ_ASSERT(sizeof(sysctlValue32) == len);
|
|
|
|
len = sizeof(sysctlValue32);
|
|
if (!sysctlbyname("machdep.cpu.model", &sysctlValue32, &len, NULL, 0)) {
|
|
cpuModel = static_cast<int>(sysctlValue32);
|
|
}
|
|
MOZ_ASSERT(sizeof(sysctlValue32) == len);
|
|
|
|
len = sizeof(sysctlValue32);
|
|
if (!sysctlbyname("machdep.cpu.stepping", &sysctlValue32, &len, NULL, 0)) {
|
|
cpuStepping = static_cast<int>(sysctlValue32);
|
|
}
|
|
MOZ_ASSERT(sizeof(sysctlValue32) == len);
|
|
|
|
#elif defined(XP_LINUX)
|
|
// Get vendor, family, model, stepping, physical cores
|
|
// from /proc/cpuinfo file
|
|
{
|
|
std::map<nsCString, nsCString> keyValuePairs;
|
|
SimpleParseKeyValuePairs("/proc/cpuinfo", keyValuePairs);
|
|
|
|
# if defined(__arm__) || defined(__aarch64__)
|
|
// The tables below were taken from
|
|
// https://raw.githubusercontent.com/util-linux/util-linux/e3192bfd1dd129c70f5416e1464135d8cd22c956/sys-utils/lscpu-arm.c
|
|
|
|
/* clang-format off */
|
|
struct id_part {
|
|
const int id;
|
|
const char* name;
|
|
};
|
|
|
|
static const struct id_part arm_part[] = {
|
|
{ 0x810, "ARM810" },
|
|
{ 0x920, "ARM920" },
|
|
{ 0x922, "ARM922" },
|
|
{ 0x926, "ARM926" },
|
|
{ 0x940, "ARM940" },
|
|
{ 0x946, "ARM946" },
|
|
{ 0x966, "ARM966" },
|
|
{ 0xa20, "ARM1020" },
|
|
{ 0xa22, "ARM1022" },
|
|
{ 0xa26, "ARM1026" },
|
|
{ 0xb02, "ARM11 MPCore" },
|
|
{ 0xb36, "ARM1136" },
|
|
{ 0xb56, "ARM1156" },
|
|
{ 0xb76, "ARM1176" },
|
|
{ 0xc05, "Cortex-A5" },
|
|
{ 0xc07, "Cortex-A7" },
|
|
{ 0xc08, "Cortex-A8" },
|
|
{ 0xc09, "Cortex-A9" },
|
|
{ 0xc0d, "Cortex-A17" }, /* Originally A12 */
|
|
{ 0xc0f, "Cortex-A15" },
|
|
{ 0xc0e, "Cortex-A17" },
|
|
{ 0xc14, "Cortex-R4" },
|
|
{ 0xc15, "Cortex-R5" },
|
|
{ 0xc17, "Cortex-R7" },
|
|
{ 0xc18, "Cortex-R8" },
|
|
{ 0xc20, "Cortex-M0" },
|
|
{ 0xc21, "Cortex-M1" },
|
|
{ 0xc23, "Cortex-M3" },
|
|
{ 0xc24, "Cortex-M4" },
|
|
{ 0xc27, "Cortex-M7" },
|
|
{ 0xc60, "Cortex-M0+" },
|
|
{ 0xd01, "Cortex-A32" },
|
|
{ 0xd02, "Cortex-A34" },
|
|
{ 0xd03, "Cortex-A53" },
|
|
{ 0xd04, "Cortex-A35" },
|
|
{ 0xd05, "Cortex-A55" },
|
|
{ 0xd06, "Cortex-A65" },
|
|
{ 0xd07, "Cortex-A57" },
|
|
{ 0xd08, "Cortex-A72" },
|
|
{ 0xd09, "Cortex-A73" },
|
|
{ 0xd0a, "Cortex-A75" },
|
|
{ 0xd0b, "Cortex-A76" },
|
|
{ 0xd0c, "Neoverse-N1" },
|
|
{ 0xd0d, "Cortex-A77" },
|
|
{ 0xd0e, "Cortex-A76AE" },
|
|
{ 0xd13, "Cortex-R52" },
|
|
{ 0xd15, "Cortex-R82" },
|
|
{ 0xd16, "Cortex-R52+" },
|
|
{ 0xd20, "Cortex-M23" },
|
|
{ 0xd21, "Cortex-M33" },
|
|
{ 0xd22, "Cortex-M55" },
|
|
{ 0xd23, "Cortex-M85" },
|
|
{ 0xd40, "Neoverse-V1" },
|
|
{ 0xd41, "Cortex-A78" },
|
|
{ 0xd42, "Cortex-A78AE" },
|
|
{ 0xd43, "Cortex-A65AE" },
|
|
{ 0xd44, "Cortex-X1" },
|
|
{ 0xd46, "Cortex-A510" },
|
|
{ 0xd47, "Cortex-A710" },
|
|
{ 0xd48, "Cortex-X2" },
|
|
{ 0xd49, "Neoverse-N2" },
|
|
{ 0xd4a, "Neoverse-E1" },
|
|
{ 0xd4b, "Cortex-A78C" },
|
|
{ 0xd4c, "Cortex-X1C" },
|
|
{ 0xd4d, "Cortex-A715" },
|
|
{ 0xd4e, "Cortex-X3" },
|
|
{ 0xd4f, "Neoverse-V2" },
|
|
{ 0xd80, "Cortex-A520" },
|
|
{ 0xd81, "Cortex-A720" },
|
|
{ 0xd82, "Cortex-X4" },
|
|
{ 0xd84, "Neoverse-V3" },
|
|
{ 0xd8e, "Neoverse-N3" },
|
|
{ -1, "unknown" },
|
|
};
|
|
|
|
static const struct id_part brcm_part[] = {
|
|
{ 0x0f, "Brahma-B15" },
|
|
{ 0x100, "Brahma-B53" },
|
|
{ 0x516, "ThunderX2" },
|
|
{ -1, "unknown" },
|
|
};
|
|
|
|
static const struct id_part dec_part[] = {
|
|
{ 0xa10, "SA110" },
|
|
{ 0xa11, "SA1100" },
|
|
{ -1, "unknown" },
|
|
};
|
|
|
|
static const struct id_part cavium_part[] = {
|
|
{ 0x0a0, "ThunderX" },
|
|
{ 0x0a1, "ThunderX-88XX" },
|
|
{ 0x0a2, "ThunderX-81XX" },
|
|
{ 0x0a3, "ThunderX-83XX" },
|
|
{ 0x0af, "ThunderX2-99xx" },
|
|
{ 0x0b0, "OcteonTX2" },
|
|
{ 0x0b1, "OcteonTX2-98XX" },
|
|
{ 0x0b2, "OcteonTX2-96XX" },
|
|
{ 0x0b3, "OcteonTX2-95XX" },
|
|
{ 0x0b4, "OcteonTX2-95XXN" },
|
|
{ 0x0b5, "OcteonTX2-95XXMM" },
|
|
{ 0x0b6, "OcteonTX2-95XXO" },
|
|
{ 0x0b8, "ThunderX3-T110" },
|
|
{ -1, "unknown" },
|
|
};
|
|
|
|
static const struct id_part apm_part[] = {
|
|
{ 0x000, "X-Gene" },
|
|
{ -1, "unknown" },
|
|
};
|
|
|
|
static const struct id_part qcom_part[] = {
|
|
{ 0x00f, "Scorpion" },
|
|
{ 0x02d, "Scorpion" },
|
|
{ 0x04d, "Krait" },
|
|
{ 0x06f, "Krait" },
|
|
{ 0x201, "Kryo" },
|
|
{ 0x205, "Kryo" },
|
|
{ 0x211, "Kryo" },
|
|
{ 0x800, "Falkor-V1/Kryo" },
|
|
{ 0x801, "Kryo-V2" },
|
|
{ 0x802, "Kryo-3XX-Gold" },
|
|
{ 0x803, "Kryo-3XX-Silver" },
|
|
{ 0x804, "Kryo-4XX-Gold" },
|
|
{ 0x805, "Kryo-4XX-Silver" },
|
|
{ 0xc00, "Falkor" },
|
|
{ 0xc01, "Saphira" },
|
|
{ -1, "unknown" },
|
|
};
|
|
|
|
static const struct id_part samsung_part[] = {
|
|
{ 0x001, "exynos-m1" },
|
|
{ 0x002, "exynos-m3" },
|
|
{ 0x003, "exynos-m4" },
|
|
{ 0x004, "exynos-m5" },
|
|
{ -1, "unknown" },
|
|
};
|
|
|
|
static const struct id_part nvidia_part[] = {
|
|
{ 0x000, "Denver" },
|
|
{ 0x003, "Denver 2" },
|
|
{ 0x004, "Carmel" },
|
|
{ -1, "unknown" },
|
|
};
|
|
|
|
static const struct id_part marvell_part[] = {
|
|
{ 0x131, "Feroceon-88FR131" },
|
|
{ 0x581, "PJ4/PJ4b" },
|
|
{ 0x584, "PJ4B-MP" },
|
|
{ -1, "unknown" },
|
|
};
|
|
|
|
static const struct id_part apple_part[] = {
|
|
{ 0x000, "Swift" },
|
|
{ 0x001, "Cyclone" },
|
|
{ 0x002, "Typhoon" },
|
|
{ 0x003, "Typhoon/Capri" },
|
|
{ 0x004, "Twister" },
|
|
{ 0x005, "Twister/Elba/Malta" },
|
|
{ 0x006, "Hurricane" },
|
|
{ 0x007, "Hurricane/Myst" },
|
|
{ 0x008, "Monsoon" },
|
|
{ 0x009, "Mistral" },
|
|
{ 0x00b, "Vortex" },
|
|
{ 0x00c, "Tempest" },
|
|
{ 0x00f, "Tempest-M9" },
|
|
{ 0x010, "Vortex/Aruba" },
|
|
{ 0x011, "Tempest/Aruba" },
|
|
{ 0x012, "Lightning" },
|
|
{ 0x013, "Thunder" },
|
|
{ 0x020, "Icestorm-A14" },
|
|
{ 0x021, "Firestorm-A14" },
|
|
{ 0x022, "Icestorm-M1" },
|
|
{ 0x023, "Firestorm-M1" },
|
|
{ 0x024, "Icestorm-M1-Pro" },
|
|
{ 0x025, "Firestorm-M1-Pro" },
|
|
{ 0x026, "Thunder-M10" },
|
|
{ 0x028, "Icestorm-M1-Max" },
|
|
{ 0x029, "Firestorm-M1-Max" },
|
|
{ 0x030, "Blizzard-A15" },
|
|
{ 0x031, "Avalanche-A15" },
|
|
{ 0x032, "Blizzard-M2" },
|
|
{ 0x033, "Avalanche-M2" },
|
|
{ 0x034, "Blizzard-M2-Pro" },
|
|
{ 0x035, "Avalanche-M2-Pro" },
|
|
{ 0x036, "Sawtooth-A16" },
|
|
{ 0x037, "Everest-A16" },
|
|
{ 0x038, "Blizzard-M2-Max" },
|
|
{ 0x039, "Avalanche-M2-Max" },
|
|
{ -1, "unknown" },
|
|
};
|
|
|
|
static const struct id_part faraday_part[] = {
|
|
{ 0x526, "FA526" },
|
|
{ 0x626, "FA626" },
|
|
{ -1, "unknown" },
|
|
};
|
|
|
|
static const struct id_part intel_part[] = {
|
|
{ 0x200, "i80200" },
|
|
{ 0x210, "PXA250A" },
|
|
{ 0x212, "PXA210A" },
|
|
{ 0x242, "i80321-400" },
|
|
{ 0x243, "i80321-600" },
|
|
{ 0x290, "PXA250B/PXA26x" },
|
|
{ 0x292, "PXA210B" },
|
|
{ 0x2c2, "i80321-400-B0" },
|
|
{ 0x2c3, "i80321-600-B0" },
|
|
{ 0x2d0, "PXA250C/PXA255/PXA26x" },
|
|
{ 0x2d2, "PXA210C" },
|
|
{ 0x411, "PXA27x" },
|
|
{ 0x41c, "IPX425-533" },
|
|
{ 0x41d, "IPX425-400" },
|
|
{ 0x41f, "IPX425-266" },
|
|
{ 0x682, "PXA32x" },
|
|
{ 0x683, "PXA930/PXA935" },
|
|
{ 0x688, "PXA30x" },
|
|
{ 0x689, "PXA31x" },
|
|
{ 0xb11, "SA1110" },
|
|
{ 0xc12, "IPX1200" },
|
|
{ -1, "unknown" },
|
|
};
|
|
|
|
static const struct id_part fujitsu_part[] = {
|
|
{ 0x001, "A64FX" },
|
|
{ -1, "unknown" },
|
|
};
|
|
|
|
static const struct id_part hisi_part[] = {
|
|
{ 0xd01, "TaiShan-v110" }, /* used in Kunpeng-920 SoC */
|
|
{ 0xd02, "TaiShan-v120" }, /* used in Kirin 990A and 9000S SoCs */
|
|
{ 0xd40, "Cortex-A76" }, /* HiSilicon uses this ID though advertises A76 */
|
|
{ 0xd41, "Cortex-A77" }, /* HiSilicon uses this ID though advertises A77 */
|
|
{ -1, "unknown" },
|
|
};
|
|
|
|
static const struct id_part ampere_part[] = {
|
|
{ 0xac3, "Ampere-1" },
|
|
{ 0xac4, "Ampere-1a" },
|
|
{ -1, "unknown" },
|
|
};
|
|
|
|
static const struct id_part ft_part[] = {
|
|
{ 0x303, "FTC310" },
|
|
{ 0x660, "FTC660" },
|
|
{ 0x661, "FTC661" },
|
|
{ 0x662, "FTC662" },
|
|
{ 0x663, "FTC663" },
|
|
{ 0x664, "FTC664" },
|
|
{ 0x862, "FTC862" },
|
|
{ -1, "unknown" },
|
|
};
|
|
|
|
static const struct id_part ms_part[] = {
|
|
{ 0xd49, "Azure-Cobalt-100" },
|
|
{ -1, "unknown" },
|
|
};
|
|
|
|
static const struct id_part unknown_part[] = {
|
|
{ -1, "unknown" },
|
|
};
|
|
|
|
struct hw_impl {
|
|
const int id;
|
|
const struct id_part *parts;
|
|
const char *name;
|
|
};
|
|
|
|
static const struct hw_impl hw_implementer[] = {
|
|
{ 0x41, arm_part, "ARM" },
|
|
{ 0x42, brcm_part, "Broadcom" },
|
|
{ 0x43, cavium_part, "Cavium" },
|
|
{ 0x44, dec_part, "DEC" },
|
|
{ 0x46, fujitsu_part, "FUJITSU" },
|
|
{ 0x48, hisi_part, "HiSilicon" },
|
|
{ 0x49, unknown_part, "Infineon" },
|
|
{ 0x4d, unknown_part, "Motorola/Freescale" },
|
|
{ 0x4e, nvidia_part, "NVIDIA" },
|
|
{ 0x50, apm_part, "APM" },
|
|
{ 0x51, qcom_part, "Qualcomm" },
|
|
{ 0x53, samsung_part, "Samsung" },
|
|
{ 0x56, marvell_part, "Marvell" },
|
|
{ 0x61, apple_part, "Apple" },
|
|
{ 0x66, faraday_part, "Faraday" },
|
|
{ 0x69, intel_part, "Intel" },
|
|
{ 0x6d, ms_part, "Microsoft" },
|
|
{ 0x70, ft_part, "Phytium" },
|
|
{ 0xc0, ampere_part, "Ampere" },
|
|
{ -1, unknown_part, "unknown" },
|
|
};
|
|
/* clang-format on */
|
|
|
|
// cpuFamily from "CPU implementer". Technically, this is only the vendor,
|
|
// but this is the closed to a family we can get.
|
|
(void)Tokenizer(keyValuePairs["CPU implementer"_ns])
|
|
.ReadHexadecimal(&cpuFamily);
|
|
|
|
// cpuModel from "CPU part". Not exactly a model number, but close enough,
|
|
// and that's what lscpu uses.
|
|
(void)Tokenizer(keyValuePairs["CPU part"_ns]).ReadHexadecimal(&cpuModel);
|
|
|
|
// cpuStepping from "CPU variant" (that's what lscpu uses).
|
|
(void)Tokenizer(keyValuePairs["CPU variant"_ns])
|
|
.ReadHexadecimal(&cpuStepping);
|
|
|
|
for (auto& hw_impl : hw_implementer) {
|
|
if (hw_impl.id == (int)cpuFamily) {
|
|
cpuVendor.Assign(hw_impl.name);
|
|
for (auto* p = &hw_impl.parts[0]; p->id != -1; ++p) {
|
|
if (p->id == (int)cpuModel) {
|
|
cpuName.Assign(p->name);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
# else
|
|
// cpuVendor from "vendor_id"
|
|
cpuVendor.Assign(keyValuePairs["vendor_id"_ns]);
|
|
|
|
// cpuName from "model name"
|
|
cpuName.Assign(keyValuePairs["model name"_ns]);
|
|
|
|
// cpuFamily from "cpu family"
|
|
(void)Tokenizer(keyValuePairs["cpu family"_ns]).ReadInteger(&cpuFamily);
|
|
|
|
// cpuModel from "model"
|
|
(void)Tokenizer(keyValuePairs["model"_ns]).ReadInteger(&cpuModel);
|
|
|
|
// cpuStepping from "stepping"
|
|
(void)Tokenizer(keyValuePairs["stepping"_ns]).ReadInteger(&cpuStepping);
|
|
# endif
|
|
}
|
|
|
|
{
|
|
// Get cpuSpeed from another file.
|
|
std::ifstream input(
|
|
"/sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_max_freq");
|
|
std::string line;
|
|
if (getline(input, line)) {
|
|
(void)Tokenizer(line.c_str()).ReadInteger(&cpuSpeed);
|
|
cpuSpeed /= 1000;
|
|
}
|
|
}
|
|
|
|
{
|
|
// Get cacheSizeL2 from yet another file
|
|
std::ifstream input("/sys/devices/system/cpu/cpu0/cache/index2/size");
|
|
std::string line;
|
|
if (getline(input, line)) {
|
|
(void)Tokenizer(line.c_str(), nullptr, "K").ReadInteger(&cacheSizeL2);
|
|
}
|
|
}
|
|
|
|
{
|
|
// Get cacheSizeL3 from yet another file
|
|
std::ifstream input("/sys/devices/system/cpu/cpu0/cache/index3/size");
|
|
std::string line;
|
|
if (getline(input, line)) {
|
|
(void)Tokenizer(line.c_str(), nullptr, "K").ReadInteger(&cacheSizeL3);
|
|
}
|
|
}
|
|
|
|
info.cpuCount = PR_GetNumberOfProcessors();
|
|
if (XRE_IsParentProcess()) {
|
|
glean::system_cpu::logical_cores.Set(info.cpuCount);
|
|
}
|
|
int max_cpu_bits = [&] {
|
|
// PR_GetNumberOfProcessors gets the value from
|
|
// /sys/devices/system/cpu/present, but the number of bits in the CPU masks
|
|
// we're going to read below can be larger (for instance, on the 32-core
|
|
// 64-threads Threadripper 3970X, PR_GetNumberOfProcessors returns 64, but
|
|
// the number of bits in the CPU masks is 128). That number of bits is
|
|
// correlated with the number of CPUs possible (which is different from the
|
|
// number of CPUs present).
|
|
std::ifstream input("/sys/devices/system/cpu/possible");
|
|
std::string line;
|
|
if (getline(input, line)) {
|
|
int num;
|
|
Tokenizer p(line.c_str());
|
|
// The expected format is `0-n` where n is the number of CPUs possible
|
|
// - 1.
|
|
if (p.ReadInteger(&num) && num == 0 && p.CheckChar('-') &&
|
|
p.ReadInteger(&num) && p.CheckEOF()) {
|
|
return num + 1;
|
|
}
|
|
}
|
|
// If we weren't able to get the value from /sys/devices/system/cpu/possible
|
|
// from some reason, fallback to cpuCount, it might work.
|
|
return info.cpuCount;
|
|
}();
|
|
|
|
// /proc/cpuinfo doesn't have a cross-architecture way of counting physical
|
|
// cores. On x86, one could look at the number of unique combinations of
|
|
// `physical id` and `core id` or `cpu cores`, but those are not present on
|
|
// e.g. aarch64. (and that might not even be enough for NUMA nodes, but
|
|
// realistically, there probably aren't a lot of people running this code
|
|
// on such machines)
|
|
// As a shortcut on x86, you'd think you could just multiply the last
|
|
// physical id + 1 with the last core id + 1, but at least core ids are not
|
|
// even necessarily adjacent. (notably, on 13th or 14th generation Intel
|
|
// CPUs, they go in increments of 4 for performance cores, and then 1 after
|
|
// hitting the first efficiency core)
|
|
// /sys/devices/system/cpu/cpu*/topology/core_cpus does show which logical
|
|
// cores are associated together, such that running the command:
|
|
// sort -u /sys/devices/system/cpu/cpu*/topology/core_cpus | wc -l
|
|
// gives a count of physical cores.
|
|
// There are cpuCount /sys/devices/system/cpu/cpu* directories, and they
|
|
// are monotonically increasing.
|
|
// We're going to kind of do that, but reading the actual bitmasks contained
|
|
// in those files.
|
|
constexpr int mask_bits = sizeof(uint32_t) * 8;
|
|
|
|
Vector<uint32_t> cpumasks;
|
|
physicalCPUs = [&] {
|
|
int cores = 0;
|
|
if (!cpumasks.appendN(0, (max_cpu_bits + mask_bits - 1) / mask_bits)) {
|
|
return -1;
|
|
}
|
|
for (int32_t cpu = 0; cpu < info.cpuCount; ++cpu) {
|
|
nsPrintfCString core_cpus(
|
|
"/sys/devices/system/cpu/cpu%d/topology/core_cpus", cpu);
|
|
std::ifstream input(core_cpus.Data());
|
|
// Kernel versions before 5.3 didn't have core_cpus, they had
|
|
// thread_siblings instead, with the same content. As of writing, kernel
|
|
// version 6.9 still has both, but thread_siblings has been deprecated
|
|
// since the introduction of core_cpus.
|
|
if (input.fail()) {
|
|
core_cpus.Truncate(core_cpus.Length() - sizeof("core_cpus") + 1);
|
|
core_cpus.AppendLiteral("thread_siblings");
|
|
input.open(core_cpus.Data());
|
|
}
|
|
std::string line;
|
|
if (!getline(input, line)) {
|
|
return -1;
|
|
}
|
|
Tokenizer p(line.c_str());
|
|
bool unknown_core = false;
|
|
// The format of the file is `bitmask0,bitmask1,..,bitmaskn`
|
|
// where each bitmask is 32-bits wide, and there are as many as
|
|
// necessary to print max_cpu_bits bits.
|
|
for (auto& mask : cpumasks) {
|
|
uint32_t m;
|
|
if (NS_WARN_IF(!p.ReadHexadecimal(&m, /* aPrefixed = */ false))) {
|
|
return -1;
|
|
}
|
|
if (!p.CheckEOF() && !p.CheckChar(',')) {
|
|
return -1;
|
|
}
|
|
// We're keeping track of all the CPU bits we've seen so far.
|
|
// If we're now seeing one that has never been set, it means
|
|
// we're seeing a new physical core (as opposed to a logical
|
|
// core). We don't want to end the loop now, though, because
|
|
// we also want to track all the bits we're seeing, in case
|
|
// subsequent masks have new bits as well.
|
|
if ((mask & m) != m) {
|
|
unknown_core = true;
|
|
}
|
|
mask |= m;
|
|
}
|
|
if (unknown_core) {
|
|
cores++;
|
|
}
|
|
}
|
|
return cores;
|
|
}();
|
|
|
|
#else
|
|
info.cpuCount = PR_GetNumberOfProcessors();
|
|
if (XRE_IsParentProcess()) {
|
|
glean::system_cpu::logical_cores.Set(info.cpuCount);
|
|
}
|
|
#endif
|
|
if (Maybe<hal::HeterogeneousCpuInfo> hetCpuInfo =
|
|
hal::GetHeterogeneousCpuInfo()) {
|
|
info.cpuPCount = int32_t(hetCpuInfo->mBigCpus.Count());
|
|
info.cpuMCount = int32_t(hetCpuInfo->mMediumCpus.Count());
|
|
info.cpuECount = int32_t(hetCpuInfo->mLittleCpus.Count());
|
|
if (XRE_IsParentProcess()) {
|
|
glean::system_cpu::big_cores.Set(info.cpuPCount);
|
|
glean::system_cpu::medium_cores.Set(info.cpuMCount);
|
|
glean::system_cpu::little_cores.Set(info.cpuECount);
|
|
}
|
|
} else {
|
|
info.cpuPCount = physicalCPUs;
|
|
if (XRE_IsParentProcess()) {
|
|
glean::system_cpu::big_cores.Set(physicalCPUs);
|
|
}
|
|
info.cpuMCount = 0;
|
|
info.cpuECount = 0;
|
|
}
|
|
|
|
if (cpuSpeed >= 0) {
|
|
info.cpuSpeed = cpuSpeed;
|
|
if (XRE_IsParentProcess()) {
|
|
glean::system_cpu::speed.Set(cpuSpeed);
|
|
}
|
|
} else {
|
|
info.cpuSpeed = 0;
|
|
}
|
|
if (!cpuVendor.IsEmpty()) {
|
|
info.cpuVendor = cpuVendor;
|
|
if (XRE_IsParentProcess()) {
|
|
glean::system_cpu::vendor.Set(cpuVendor);
|
|
}
|
|
}
|
|
if (!cpuName.IsEmpty()) {
|
|
info.cpuName = cpuName;
|
|
if (XRE_IsParentProcess()) {
|
|
glean::system_cpu::name.Set(cpuName);
|
|
}
|
|
}
|
|
if (cpuFamily >= 0) {
|
|
info.cpuFamily = cpuFamily;
|
|
if (XRE_IsParentProcess()) {
|
|
glean::system_cpu::family.Set(cpuFamily);
|
|
}
|
|
}
|
|
if (cpuModel >= 0) {
|
|
info.cpuModel = cpuModel;
|
|
if (XRE_IsParentProcess()) {
|
|
glean::system_cpu::model.Set(cpuModel);
|
|
}
|
|
}
|
|
if (cpuStepping >= 0) {
|
|
info.cpuStepping = cpuStepping;
|
|
if (XRE_IsParentProcess()) {
|
|
glean::system_cpu::stepping.Set(cpuStepping);
|
|
}
|
|
}
|
|
|
|
if (logicalCPUs >= 0) {
|
|
info.cpuCount = logicalCPUs;
|
|
if (XRE_IsParentProcess()) {
|
|
glean::system_cpu::logical_cores.Set(logicalCPUs);
|
|
}
|
|
}
|
|
if (physicalCPUs >= 0) {
|
|
info.cpuCores = physicalCPUs;
|
|
if (XRE_IsParentProcess()) {
|
|
glean::system_cpu::physical_cores.Set(physicalCPUs);
|
|
}
|
|
}
|
|
|
|
if (cacheSizeL2 >= 0) {
|
|
info.l2cacheKB = cacheSizeL2;
|
|
if (XRE_IsParentProcess()) {
|
|
glean::system_cpu::l2_cache.Set(cacheSizeL2);
|
|
}
|
|
}
|
|
if (cacheSizeL3 >= 0) {
|
|
info.l3cacheKB = cacheSizeL3;
|
|
if (XRE_IsParentProcess()) {
|
|
glean::system_cpu::l3_cache.Set(cacheSizeL3);
|
|
}
|
|
}
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
#if defined(__MINGW32__)
|
|
WINBASEAPI
|
|
BOOL WINAPI IsUserCetAvailableInEnvironment(_In_ DWORD UserCetEnvironment);
|
|
|
|
# define USER_CET_ENVIRONMENT_WIN32_PROCESS 0x00000000
|
|
#endif
|
|
|
|
nsresult nsSystemInfo::Init() {
|
|
// check that it is called from the main thread on all platforms.
|
|
MOZ_ASSERT(NS_IsMainThread());
|
|
|
|
nsresult rv;
|
|
|
|
static const struct {
|
|
PRSysInfo cmd;
|
|
const char* name;
|
|
} items[] = {{PR_SI_SYSNAME, "name"},
|
|
{PR_SI_ARCHITECTURE, "arch"},
|
|
{PR_SI_RELEASE, "version"},
|
|
{PR_SI_RELEASE_BUILD, "build"}};
|
|
|
|
for (uint32_t i = 0; i < (sizeof(items) / sizeof(items[0])); i++) {
|
|
char buf[SYS_INFO_BUFFER_LENGTH];
|
|
if (PR_GetSystemInfo(items[i].cmd, buf, sizeof(buf)) == PR_SUCCESS) {
|
|
rv = SetPropertyAsACString(NS_ConvertASCIItoUTF16(items[i].name),
|
|
nsDependentCString(buf));
|
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
|
return rv;
|
|
}
|
|
} else {
|
|
NS_WARNING("PR_GetSystemInfo failed");
|
|
}
|
|
}
|
|
|
|
SetPropertyAsBool(u"isPackagedApp"_ns, false);
|
|
|
|
// Additional informations not available through PR_GetSystemInfo.
|
|
SetInt32Property(u"pagesize"_ns, PR_GetPageSize());
|
|
SetInt32Property(u"pageshift"_ns, PR_GetPageShift());
|
|
SetInt32Property(u"memmapalign"_ns, PR_GetMemMapAlignment());
|
|
SetUint64Property(u"memsize"_ns, PR_GetPhysicalMemorySize());
|
|
SetUint32Property(u"umask"_ns, nsSystemInfo::gUserUmask);
|
|
|
|
#ifdef HAVE_64BIT_BUILD
|
|
SetUint32Property(u"archbits"_ns, 64);
|
|
#else
|
|
SetUint32Property(u"archbits"_ns, 32);
|
|
#endif
|
|
|
|
uint64_t virtualMem = 0;
|
|
|
|
#if defined(XP_WIN)
|
|
// Virtual memory:
|
|
MEMORYSTATUSEX memStat;
|
|
memStat.dwLength = sizeof(memStat);
|
|
if (GlobalMemoryStatusEx(&memStat)) {
|
|
virtualMem = memStat.ullTotalVirtual;
|
|
}
|
|
#endif
|
|
if (virtualMem) SetUint64Property(u"virtualmemsize"_ns, virtualMem);
|
|
|
|
for (uint32_t i = 0; i < std::size(cpuPropItems); i++) {
|
|
rv = SetPropertyAsBool(NS_ConvertASCIItoUTF16(cpuPropItems[i].name),
|
|
cpuPropItems[i].propfun());
|
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
|
return rv;
|
|
}
|
|
}
|
|
|
|
#ifdef XP_WIN
|
|
bool isMinGW =
|
|
# ifdef __MINGW32__
|
|
true;
|
|
# else
|
|
false;
|
|
# endif
|
|
rv = SetPropertyAsBool(u"isMinGW"_ns, !!isMinGW);
|
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
|
return rv;
|
|
}
|
|
|
|
boolean hasPackageIdentity = widget::WinUtils::HasPackageIdentity();
|
|
|
|
rv = SetPropertyAsBool(u"hasWinPackageId"_ns, hasPackageIdentity);
|
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
|
return rv;
|
|
}
|
|
|
|
rv = SetPropertyAsAString(u"winPackageFamilyName"_ns,
|
|
widget::WinUtils::GetPackageFamilyName());
|
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
|
return rv;
|
|
}
|
|
|
|
rv = SetPropertyAsBool(u"isPackagedApp"_ns, hasPackageIdentity);
|
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
|
return rv;
|
|
}
|
|
|
|
# ifndef __MINGW32__
|
|
nsAutoString avInfo, antiSpyInfo, firewallInfo;
|
|
if (NS_SUCCEEDED(
|
|
GetWindowsSecurityCenterInfo(avInfo, antiSpyInfo, firewallInfo))) {
|
|
if (!avInfo.IsEmpty()) {
|
|
rv = SetPropertyAsAString(u"registeredAntiVirus"_ns, avInfo);
|
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
|
return rv;
|
|
}
|
|
}
|
|
|
|
if (!antiSpyInfo.IsEmpty()) {
|
|
rv = SetPropertyAsAString(u"registeredAntiSpyware"_ns, antiSpyInfo);
|
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
|
return rv;
|
|
}
|
|
}
|
|
|
|
if (!firewallInfo.IsEmpty()) {
|
|
rv = SetPropertyAsAString(u"registeredFirewall"_ns, firewallInfo);
|
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
|
return rv;
|
|
}
|
|
}
|
|
}
|
|
# endif // __MINGW32__
|
|
|
|
mozilla::DynamicallyLinkedFunctionPtr<
|
|
decltype(&IsUserCetAvailableInEnvironment)>
|
|
isUserCetAvailable(L"api-ms-win-core-sysinfo-l1-2-6.dll",
|
|
"IsUserCetAvailableInEnvironment");
|
|
bool hasUserCET = isUserCetAvailable &&
|
|
isUserCetAvailable(USER_CET_ENVIRONMENT_WIN32_PROCESS);
|
|
rv = SetPropertyAsBool(u"hasUserCET"_ns, hasUserCET);
|
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
|
return rv;
|
|
}
|
|
|
|
#endif
|
|
|
|
#if defined(XP_WIN) || defined(ANDROID)
|
|
// TODO(krosylight): Enable this on other platforms too when implemented
|
|
if (XRE_IsParentProcess()) {
|
|
auto kinds = static_cast<LookAndFeel::PointingDeviceKinds>(
|
|
LookAndFeel::GetInt(LookAndFeel::IntID::PointingDeviceKinds, 0));
|
|
MOZ_TRY(SetPropertyAsBool(
|
|
u"hasMouse"_ns, !!(kinds & LookAndFeel::PointingDeviceKinds::Mouse)));
|
|
MOZ_TRY(SetPropertyAsBool(
|
|
u"hasTouch"_ns, !!(kinds & LookAndFeel::PointingDeviceKinds::Touch)));
|
|
MOZ_TRY(SetPropertyAsBool(
|
|
u"hasPen"_ns, !!(kinds & LookAndFeel::PointingDeviceKinds::Pen)));
|
|
}
|
|
#endif
|
|
|
|
#if defined(XP_MACOSX)
|
|
nsAutoCString modelId;
|
|
if (NS_SUCCEEDED(GetAppleModelId(modelId))) {
|
|
rv = SetPropertyAsACString(u"appleModelId"_ns, modelId);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
}
|
|
bool isRosetta;
|
|
if (NS_SUCCEEDED(ProcessIsRosettaTranslated(isRosetta))) {
|
|
rv = SetPropertyAsBool(u"rosettaStatus"_ns, isRosetta);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
}
|
|
#endif
|
|
|
|
#if defined(XP_WIN)
|
|
nsAutoString modelId;
|
|
if (NS_SUCCEEDED(GetWinModelId(modelId))) {
|
|
rv = SetPropertyAsAString(u"winModelId"_ns, modelId);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
}
|
|
#endif
|
|
|
|
#if defined(XP_LINUX)
|
|
nsAutoCString productName;
|
|
if (NS_SUCCEEDED(GetLinuxProductName(productName))) {
|
|
rv = SetPropertyAsACString(u"linuxProductName"_ns, productName);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
}
|
|
|
|
nsAutoCString productSku;
|
|
if (NS_SUCCEEDED(GetLinuxProductSku(productSku))) {
|
|
rv = SetPropertyAsACString(u"linuxProductSku"_ns, productSku);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
}
|
|
#endif
|
|
|
|
{
|
|
nsAutoCString themeInfo;
|
|
LookAndFeel::GetThemeInfo(themeInfo);
|
|
MOZ_TRY(SetPropertyAsACString(u"osThemeInfo"_ns, themeInfo));
|
|
}
|
|
|
|
#if defined(MOZ_WIDGET_GTK)
|
|
// This must be done here because NSPR can only separate OS's when compiled,
|
|
// not libraries. 64 bytes is going to be well enough for "GTK " followed by 3
|
|
// integers separated with dots.
|
|
char gtkver[64];
|
|
ssize_t gtkver_len = 0;
|
|
|
|
if (gtkver_len <= 0) {
|
|
gtkver_len = SprintfLiteral(gtkver, "GTK %u.%u.%u", gtk_major_version,
|
|
gtk_minor_version, gtk_micro_version);
|
|
}
|
|
|
|
nsAutoCString secondaryLibrary;
|
|
if (gtkver_len > 0 && gtkver_len < int(sizeof(gtkver))) {
|
|
secondaryLibrary.Append(nsDependentCSubstring(gtkver, gtkver_len));
|
|
}
|
|
|
|
# ifndef MOZ_TSAN
|
|
// With TSan, avoid loading libpulse here because we cannot unload it
|
|
// afterwards due to restrictions from TSan about unloading libraries
|
|
// matched by the suppression list.
|
|
void* libpulse = dlopen("libpulse.so.0", RTLD_LAZY);
|
|
const char* libpulseVersion = "not-available";
|
|
if (libpulse) {
|
|
auto pa_get_library_version = reinterpret_cast<const char* (*)()>(
|
|
dlsym(libpulse, "pa_get_library_version"));
|
|
|
|
if (pa_get_library_version) {
|
|
libpulseVersion = pa_get_library_version();
|
|
}
|
|
}
|
|
|
|
secondaryLibrary.AppendPrintf(",libpulse %s", libpulseVersion);
|
|
|
|
if (libpulse) {
|
|
dlclose(libpulse);
|
|
}
|
|
# endif
|
|
|
|
rv = SetPropertyAsACString(u"secondaryLibrary"_ns, secondaryLibrary);
|
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
|
return rv;
|
|
}
|
|
rv = SetPropertyAsBool(u"isPackagedApp"_ns,
|
|
widget::IsRunningUnderFlatpakOrSnap() ||
|
|
widget::IsPackagedAppFileExists());
|
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
|
return rv;
|
|
}
|
|
#endif
|
|
|
|
#ifdef MOZ_WIDGET_ANDROID
|
|
AndroidSystemInfo info;
|
|
GetAndroidSystemInfo(&info);
|
|
SetupAndroidInfo(info);
|
|
#endif
|
|
|
|
#if defined(XP_LINUX) && !defined(ANDROID)
|
|
nsCString dist, desc, release, codename;
|
|
if (widget::lsb::GetLSBRelease(dist, desc, release, codename)) {
|
|
SetPropertyAsACString(u"distro"_ns, dist);
|
|
SetPropertyAsACString(u"distroVersion"_ns, release);
|
|
}
|
|
#endif
|
|
|
|
#if defined(XP_LINUX) && defined(MOZ_SANDBOX)
|
|
SandboxInfo sandInfo = SandboxInfo::Get();
|
|
|
|
SetPropertyAsBool(u"hasSeccompBPF"_ns,
|
|
sandInfo.Test(SandboxInfo::kHasSeccompBPF));
|
|
SetPropertyAsBool(u"hasSeccompTSync"_ns,
|
|
sandInfo.Test(SandboxInfo::kHasSeccompTSync));
|
|
SetPropertyAsBool(u"hasUserNamespaces"_ns,
|
|
sandInfo.Test(SandboxInfo::kHasUserNamespaces));
|
|
SetPropertyAsBool(u"hasPrivilegedUserNamespaces"_ns,
|
|
sandInfo.Test(SandboxInfo::kHasPrivilegedUserNamespaces));
|
|
|
|
if (sandInfo.Test(SandboxInfo::kEnabledForContent)) {
|
|
SetPropertyAsBool(u"canSandboxContent"_ns, sandInfo.CanSandboxContent());
|
|
}
|
|
|
|
if (sandInfo.Test(SandboxInfo::kEnabledForMedia)) {
|
|
SetPropertyAsBool(u"canSandboxMedia"_ns, sandInfo.CanSandboxMedia());
|
|
}
|
|
#endif // XP_LINUX && MOZ_SANDBOX
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
#ifdef MOZ_WIDGET_ANDROID
|
|
// Prerelease versions of Android use a letter instead of version numbers.
|
|
// Unfortunately this breaks websites due to the user agent.
|
|
// Chrome works around this by hardcoding an Android version when a
|
|
// numeric version can't be obtained. We're doing the same.
|
|
// This version will need to be updated whenever there is a new official
|
|
// Android release. Search for "kDefaultAndroidMajorVersion" in:
|
|
// https://source.chromium.org/chromium/chromium/src/+/master:base/system/sys_info_android.cc
|
|
# define DEFAULT_ANDROID_VERSION u"10.0.99"
|
|
|
|
/* static */
|
|
void nsSystemInfo::GetAndroidSystemInfo(AndroidSystemInfo* aInfo) {
|
|
if (!jni::IsAvailable()) {
|
|
// called from xpcshell etc.
|
|
aInfo->sdk_version() = 0;
|
|
return;
|
|
}
|
|
|
|
jni::String::LocalRef model = java::sdk::Build::MODEL();
|
|
aInfo->device() = model->ToString();
|
|
|
|
jni::String::LocalRef manufacturer =
|
|
mozilla::java::sdk::Build::MANUFACTURER();
|
|
aInfo->manufacturer() = manufacturer->ToString();
|
|
|
|
jni::String::LocalRef hardware = java::sdk::Build::HARDWARE();
|
|
aInfo->hardware() = hardware->ToString();
|
|
|
|
jni::String::LocalRef release = java::sdk::Build::VERSION::RELEASE();
|
|
nsString str(release->ToString());
|
|
int major_version;
|
|
int minor_version;
|
|
int bugfix_version;
|
|
int num_read = sscanf(NS_ConvertUTF16toUTF8(str).get(), "%d.%d.%d",
|
|
&major_version, &minor_version, &bugfix_version);
|
|
if (num_read == 0) {
|
|
aInfo->release_version() = nsLiteralString(DEFAULT_ANDROID_VERSION);
|
|
} else {
|
|
aInfo->release_version() = str;
|
|
}
|
|
|
|
aInfo->sdk_version() = jni::GetAPIVersion();
|
|
aInfo->isTablet() = java::GeckoAppShell::IsTablet();
|
|
}
|
|
|
|
void nsSystemInfo::SetupAndroidInfo(const AndroidSystemInfo& aInfo) {
|
|
if (!aInfo.device().IsEmpty()) {
|
|
SetPropertyAsAString(u"device"_ns, aInfo.device());
|
|
}
|
|
if (!aInfo.manufacturer().IsEmpty()) {
|
|
SetPropertyAsAString(u"manufacturer"_ns, aInfo.manufacturer());
|
|
}
|
|
if (!aInfo.release_version().IsEmpty()) {
|
|
SetPropertyAsAString(u"release_version"_ns, aInfo.release_version());
|
|
}
|
|
SetPropertyAsBool(u"tablet"_ns, aInfo.isTablet());
|
|
// NSPR "version" is the kernel version. For Android we want the Android
|
|
// version. Rename SDK version to version and put the kernel version into
|
|
// kernel_version.
|
|
nsAutoString str;
|
|
nsresult rv = GetPropertyAsAString(u"version"_ns, str);
|
|
if (NS_SUCCEEDED(rv)) {
|
|
SetPropertyAsAString(u"kernel_version"_ns, str);
|
|
}
|
|
// When JNI is not available (eg. in xpcshell tests), sdk_version is 0.
|
|
if (aInfo.sdk_version() != 0) {
|
|
if (!aInfo.hardware().IsEmpty()) {
|
|
SetPropertyAsAString(u"hardware"_ns, aInfo.hardware());
|
|
}
|
|
SetPropertyAsInt32(u"version"_ns, aInfo.sdk_version());
|
|
}
|
|
}
|
|
#endif // MOZ_WIDGET_ANDROID
|
|
|
|
void nsSystemInfo::SetInt32Property(const nsAString& aPropertyName,
|
|
const int32_t aValue) {
|
|
NS_WARNING_ASSERTION(aValue > 0, "Unable to read system value");
|
|
if (aValue > 0) {
|
|
#ifdef DEBUG
|
|
nsresult rv =
|
|
#endif
|
|
SetPropertyAsInt32(aPropertyName, aValue);
|
|
NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "Unable to set property");
|
|
}
|
|
}
|
|
|
|
void nsSystemInfo::SetUint32Property(const nsAString& aPropertyName,
|
|
const uint32_t aValue) {
|
|
// Only one property is currently set via this function.
|
|
// It may legitimately be zero.
|
|
#ifdef DEBUG
|
|
nsresult rv =
|
|
#endif
|
|
SetPropertyAsUint32(aPropertyName, aValue);
|
|
NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "Unable to set property");
|
|
}
|
|
|
|
void nsSystemInfo::SetUint64Property(const nsAString& aPropertyName,
|
|
const uint64_t aValue) {
|
|
NS_WARNING_ASSERTION(aValue > 0, "Unable to read system value");
|
|
if (aValue > 0) {
|
|
#ifdef DEBUG
|
|
nsresult rv =
|
|
#endif
|
|
SetPropertyAsUint64(aPropertyName, aValue);
|
|
NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "Unable to set property");
|
|
}
|
|
}
|
|
|
|
#ifdef XP_WIN
|
|
|
|
static bool GetJSObjForDiskInfo(JSContext* aCx, JS::Handle<JSObject*> aParent,
|
|
const FolderDiskInfo& info,
|
|
const char* propName) {
|
|
JS::Rooted<JSObject*> jsInfo(aCx, JS_NewPlainObject(aCx));
|
|
if (!jsInfo) {
|
|
return false;
|
|
}
|
|
|
|
JSString* strModel =
|
|
JS_NewStringCopyN(aCx, info.model.get(), info.model.Length());
|
|
if (!strModel) {
|
|
return false;
|
|
}
|
|
JS::Rooted<JS::Value> valModel(aCx, JS::StringValue(strModel));
|
|
if (!JS_SetProperty(aCx, jsInfo, "model", valModel)) {
|
|
return false;
|
|
}
|
|
|
|
JSString* strRevision =
|
|
JS_NewStringCopyN(aCx, info.revision.get(), info.revision.Length());
|
|
if (!strRevision) {
|
|
return false;
|
|
}
|
|
JS::Rooted<JS::Value> valRevision(aCx, JS::StringValue(strRevision));
|
|
if (!JS_SetProperty(aCx, jsInfo, "revision", valRevision)) {
|
|
return false;
|
|
}
|
|
|
|
JSString* strSSD = JS_NewStringCopyZ(aCx, info.isSSD ? "SSD" : "HDD");
|
|
if (!strSSD) {
|
|
return false;
|
|
}
|
|
JS::Rooted<JS::Value> valSSD(aCx, JS::StringValue(strSSD));
|
|
if (!JS_SetProperty(aCx, jsInfo, "type", valSSD)) {
|
|
return false;
|
|
}
|
|
|
|
JS::Rooted<JS::Value> val(aCx, JS::ObjectValue(*jsInfo));
|
|
return JS_SetProperty(aCx, aParent, propName, val);
|
|
}
|
|
|
|
JSObject* GetJSObjForOSInfo(JSContext* aCx, const OSInfo& info) {
|
|
JS::Rooted<JSObject*> jsInfo(aCx, JS_NewPlainObject(aCx));
|
|
|
|
JS::Rooted<JS::Value> valInstallYear(aCx, JS::Int32Value(info.installYear));
|
|
JS_SetProperty(aCx, jsInfo, "installYear", valInstallYear);
|
|
|
|
JS::Rooted<JS::Value> valHasSuperfetch(aCx,
|
|
JS::BooleanValue(info.hasSuperfetch));
|
|
JS_SetProperty(aCx, jsInfo, "hasSuperfetch", valHasSuperfetch);
|
|
|
|
JS::Rooted<JS::Value> valHasPrefetch(aCx, JS::BooleanValue(info.hasPrefetch));
|
|
JS_SetProperty(aCx, jsInfo, "hasPrefetch", valHasPrefetch);
|
|
|
|
return jsInfo;
|
|
}
|
|
|
|
#endif
|
|
|
|
JSObject* GetJSObjForProcessInfo(JSContext* aCx, const ProcessInfo& info) {
|
|
JS::Rooted<JSObject*> jsInfo(aCx, JS_NewPlainObject(aCx));
|
|
|
|
#if defined(XP_WIN)
|
|
JS::Rooted<JS::Value> valisWow64(aCx, JS::BooleanValue(info.isWow64));
|
|
JS_SetProperty(aCx, jsInfo, "isWow64", valisWow64);
|
|
|
|
JS::Rooted<JS::Value> valisWowARM64(aCx, JS::BooleanValue(info.isWowARM64));
|
|
JS_SetProperty(aCx, jsInfo, "isWowARM64", valisWowARM64);
|
|
|
|
JS::Rooted<JS::Value> valisWindowsSMode(
|
|
aCx, JS::BooleanValue(info.isWindowsSMode));
|
|
JS_SetProperty(aCx, jsInfo, "isWindowsSMode", valisWindowsSMode);
|
|
#endif
|
|
|
|
JS::Rooted<JS::Value> valCountInfo(aCx, JS::Int32Value(info.cpuCount));
|
|
JS_SetProperty(aCx, jsInfo, "count", valCountInfo);
|
|
|
|
JS::Rooted<JS::Value> valCoreInfo(
|
|
aCx, info.cpuCores ? JS::Int32Value(info.cpuCores) : JS::NullValue());
|
|
JS_SetProperty(aCx, jsInfo, "cores", valCoreInfo);
|
|
|
|
JS::Rooted<JS::Value> valPCountInfo(
|
|
aCx, info.cpuCores ? JS::Int32Value(info.cpuPCount) : JS::NullValue());
|
|
JS_SetProperty(aCx, jsInfo, "pcount", valPCountInfo);
|
|
|
|
JS::Rooted<JS::Value> valMCountInfo(
|
|
aCx, info.cpuCores ? JS::Int32Value(info.cpuMCount) : JS::NullValue());
|
|
JS_SetProperty(aCx, jsInfo, "mcount", valMCountInfo);
|
|
|
|
JS::Rooted<JS::Value> valECountInfo(
|
|
aCx, info.cpuCores ? JS::Int32Value(info.cpuECount) : JS::NullValue());
|
|
JS_SetProperty(aCx, jsInfo, "ecount", valECountInfo);
|
|
|
|
JSString* strVendor =
|
|
JS_NewStringCopyN(aCx, info.cpuVendor.get(), info.cpuVendor.Length());
|
|
JS::Rooted<JS::Value> valVendor(aCx, JS::StringValue(strVendor));
|
|
JS_SetProperty(aCx, jsInfo, "vendor", valVendor);
|
|
|
|
JSString* strName =
|
|
JS_NewStringCopyN(aCx, info.cpuName.get(), info.cpuName.Length());
|
|
JS::Rooted<JS::Value> valName(aCx, JS::StringValue(strName));
|
|
JS_SetProperty(aCx, jsInfo, "name", valName);
|
|
|
|
JS::Rooted<JS::Value> valFamilyInfo(aCx, JS::Int32Value(info.cpuFamily));
|
|
JS_SetProperty(aCx, jsInfo, "family", valFamilyInfo);
|
|
|
|
JS::Rooted<JS::Value> valModelInfo(aCx, JS::Int32Value(info.cpuModel));
|
|
JS_SetProperty(aCx, jsInfo, "model", valModelInfo);
|
|
|
|
JS::Rooted<JS::Value> valSteppingInfo(aCx, JS::Int32Value(info.cpuStepping));
|
|
JS_SetProperty(aCx, jsInfo, "stepping", valSteppingInfo);
|
|
|
|
JS::Rooted<JS::Value> valL2CacheInfo(aCx, JS::Int32Value(info.l2cacheKB));
|
|
JS_SetProperty(aCx, jsInfo, "l2cacheKB", valL2CacheInfo);
|
|
|
|
JS::Rooted<JS::Value> valL3CacheInfo(aCx, JS::Int32Value(info.l3cacheKB));
|
|
JS_SetProperty(aCx, jsInfo, "l3cacheKB", valL3CacheInfo);
|
|
|
|
JS::Rooted<JS::Value> valSpeedInfo(aCx, JS::Int32Value(info.cpuSpeed));
|
|
JS_SetProperty(aCx, jsInfo, "speedMHz", valSpeedInfo);
|
|
|
|
return jsInfo;
|
|
}
|
|
|
|
RefPtr<nsISerialEventTarget> nsSystemInfo::GetBackgroundTarget() {
|
|
if (!mBackgroundET) {
|
|
MOZ_ALWAYS_SUCCEEDS(NS_CreateBackgroundTaskQueue(
|
|
"SystemInfoThread", getter_AddRefs(mBackgroundET)));
|
|
}
|
|
return mBackgroundET;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsSystemInfo::GetOsInfo(JSContext* aCx, Promise** aResult) {
|
|
NS_ENSURE_ARG_POINTER(aResult);
|
|
*aResult = nullptr;
|
|
if (!XRE_IsParentProcess()) {
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
#if defined(XP_WIN)
|
|
nsIGlobalObject* global = xpc::CurrentNativeGlobal(aCx);
|
|
if (NS_WARN_IF(!global)) {
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
|
|
ErrorResult erv;
|
|
RefPtr<Promise> promise = Promise::Create(global, erv);
|
|
if (NS_WARN_IF(erv.Failed())) {
|
|
return erv.StealNSResult();
|
|
}
|
|
|
|
if (!mOSInfoPromise) {
|
|
RefPtr<nsISerialEventTarget> backgroundET = GetBackgroundTarget();
|
|
|
|
mOSInfoPromise = InvokeAsync(backgroundET, __func__, []() {
|
|
OSInfo info;
|
|
nsresult rv = CollectOSInfo(info);
|
|
if (NS_SUCCEEDED(rv)) {
|
|
return OSInfoPromise::CreateAndResolve(info, __func__);
|
|
}
|
|
return OSInfoPromise::CreateAndReject(rv, __func__);
|
|
});
|
|
};
|
|
|
|
auto requestHolder =
|
|
MakeRefPtr<dom::DOMMozPromiseRequestHolder<OSInfoPromise>>(global);
|
|
|
|
// Chain the new promise to the extant mozpromise
|
|
RefPtr<Promise> capturedPromise = promise;
|
|
mOSInfoPromise
|
|
->Then(
|
|
GetMainThreadSerialEventTarget(), __func__,
|
|
[requestHolder, capturedPromise](const OSInfo& info) {
|
|
requestHolder->Complete();
|
|
AutoJSAPI jsapi;
|
|
if (NS_WARN_IF(!jsapi.Init(capturedPromise->GetGlobalObject()))) {
|
|
capturedPromise->MaybeReject(NS_ERROR_UNEXPECTED);
|
|
return;
|
|
}
|
|
JSContext* cx = jsapi.cx();
|
|
JS::Rooted<JS::Value> val(
|
|
cx, JS::ObjectValue(*GetJSObjForOSInfo(cx, info)));
|
|
capturedPromise->MaybeResolve(val);
|
|
},
|
|
[requestHolder, capturedPromise](const nsresult rv) {
|
|
requestHolder->Complete();
|
|
// Resolve with null when installYear is not available from the
|
|
// system
|
|
capturedPromise->MaybeResolve(JS::NullHandleValue);
|
|
})
|
|
->Track(*requestHolder);
|
|
|
|
promise.forget(aResult);
|
|
#endif
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsSystemInfo::GetDiskInfo(JSContext* aCx, Promise** aResult) {
|
|
NS_ENSURE_ARG_POINTER(aResult);
|
|
*aResult = nullptr;
|
|
if (!XRE_IsParentProcess()) {
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
#ifdef XP_WIN
|
|
nsIGlobalObject* global = xpc::CurrentNativeGlobal(aCx);
|
|
if (NS_WARN_IF(!global)) {
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
ErrorResult erv;
|
|
RefPtr<Promise> promise = Promise::Create(global, erv);
|
|
if (NS_WARN_IF(erv.Failed())) {
|
|
return erv.StealNSResult();
|
|
}
|
|
|
|
if (!mDiskInfoPromise) {
|
|
RefPtr<nsISerialEventTarget> backgroundET = GetBackgroundTarget();
|
|
nsCOMPtr<nsIFile> greDir;
|
|
nsCOMPtr<nsIFile> winDir;
|
|
nsCOMPtr<nsIFile> profDir;
|
|
nsresult rv = NS_GetSpecialDirectory(NS_GRE_DIR, getter_AddRefs(greDir));
|
|
if (NS_FAILED(rv)) {
|
|
return rv;
|
|
}
|
|
rv = NS_GetSpecialDirectory(NS_WIN_WINDOWS_DIR, getter_AddRefs(winDir));
|
|
if (NS_FAILED(rv)) {
|
|
return rv;
|
|
}
|
|
rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR,
|
|
getter_AddRefs(profDir));
|
|
if (NS_FAILED(rv)) {
|
|
return rv;
|
|
}
|
|
|
|
mDiskInfoPromise =
|
|
InvokeAsync(backgroundET, __func__, [greDir, winDir, profDir]() {
|
|
DiskInfo info;
|
|
nsresult rv = CollectDiskInfo(greDir, winDir, profDir, info);
|
|
if (NS_SUCCEEDED(rv)) {
|
|
return DiskInfoPromise::CreateAndResolve(info, __func__);
|
|
}
|
|
return DiskInfoPromise::CreateAndReject(rv, __func__);
|
|
});
|
|
}
|
|
|
|
auto requestHolder =
|
|
MakeRefPtr<dom::DOMMozPromiseRequestHolder<DiskInfoPromise>>(global);
|
|
|
|
// Chain the new promise to the extant mozpromise.
|
|
RefPtr<Promise> capturedPromise = promise;
|
|
mDiskInfoPromise
|
|
->Then(
|
|
GetMainThreadSerialEventTarget(), __func__,
|
|
[requestHolder, capturedPromise,
|
|
self = RefPtr{this}](const DiskInfo& info) {
|
|
requestHolder->Complete();
|
|
AutoJSAPI jsapi;
|
|
if (NS_WARN_IF(!jsapi.Init(capturedPromise->GetGlobalObject()))) {
|
|
capturedPromise->MaybeReject(NS_ERROR_UNEXPECTED);
|
|
return;
|
|
}
|
|
JSContext* cx = jsapi.cx();
|
|
JS::Rooted<JSObject*> jsInfo(cx, JS_NewPlainObject(cx));
|
|
// Store data in the rv:
|
|
bool succeededSettingAllObjects =
|
|
jsInfo &&
|
|
GetJSObjForDiskInfo(cx, jsInfo, info.binary, "binary") &&
|
|
GetJSObjForDiskInfo(cx, jsInfo, info.profile, "profile") &&
|
|
GetJSObjForDiskInfo(cx, jsInfo, info.system, "system");
|
|
// The above can fail due to OOM
|
|
if (!succeededSettingAllObjects) {
|
|
JS_ClearPendingException(cx);
|
|
capturedPromise->MaybeReject(NS_ERROR_FAILURE);
|
|
return;
|
|
}
|
|
|
|
bool hasSSD =
|
|
info.binary.isSSD || info.system.isSSD || info.profile.isSSD;
|
|
self->SetPropertyAsBool(u"hasSSD"_ns, hasSSD);
|
|
|
|
JS::Rooted<JS::Value> val(cx, JS::ObjectValue(*jsInfo));
|
|
capturedPromise->MaybeResolve(val);
|
|
},
|
|
[requestHolder, capturedPromise](const nsresult rv) {
|
|
requestHolder->Complete();
|
|
capturedPromise->MaybeReject(rv);
|
|
})
|
|
->Track(*requestHolder);
|
|
|
|
promise.forget(aResult);
|
|
#endif
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMPL_ISUPPORTS_INHERITED(nsSystemInfo, nsHashPropertyBag, nsISystemInfo)
|
|
|
|
NS_IMETHODIMP
|
|
nsSystemInfo::GetCountryCode(JSContext* aCx, Promise** aResult) {
|
|
NS_ENSURE_ARG_POINTER(aResult);
|
|
*aResult = nullptr;
|
|
|
|
if (!XRE_IsParentProcess()) {
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
#if defined(XP_MACOSX) || defined(XP_WIN)
|
|
nsIGlobalObject* global = xpc::CurrentNativeGlobal(aCx);
|
|
if (NS_WARN_IF(!global)) {
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
|
|
ErrorResult erv;
|
|
RefPtr<Promise> promise = Promise::Create(global, erv);
|
|
if (NS_WARN_IF(erv.Failed())) {
|
|
return erv.StealNSResult();
|
|
}
|
|
|
|
if (!mCountryCodePromise) {
|
|
RefPtr<nsISerialEventTarget> backgroundET = GetBackgroundTarget();
|
|
|
|
mCountryCodePromise = InvokeAsync(backgroundET, __func__, []() {
|
|
nsAutoString countryCode;
|
|
# ifdef XP_MACOSX
|
|
nsresult rv = GetSelectedCityInfo(countryCode);
|
|
# endif
|
|
# ifdef XP_WIN
|
|
nsresult rv = CollectCountryCode(countryCode);
|
|
# endif
|
|
|
|
if (NS_SUCCEEDED(rv)) {
|
|
return CountryCodePromise::CreateAndResolve(countryCode, __func__);
|
|
}
|
|
return CountryCodePromise::CreateAndReject(rv, __func__);
|
|
});
|
|
}
|
|
|
|
auto requestHolder =
|
|
MakeRefPtr<dom::DOMMozPromiseRequestHolder<CountryCodePromise>>(global);
|
|
|
|
RefPtr<Promise> capturedPromise = promise;
|
|
mCountryCodePromise
|
|
->Then(
|
|
GetMainThreadSerialEventTarget(), __func__,
|
|
[requestHolder, capturedPromise](const nsString& countryCode) {
|
|
requestHolder->Complete();
|
|
AutoJSAPI jsapi;
|
|
if (NS_WARN_IF(!jsapi.Init(capturedPromise->GetGlobalObject()))) {
|
|
capturedPromise->MaybeReject(NS_ERROR_UNEXPECTED);
|
|
return;
|
|
}
|
|
JSContext* cx = jsapi.cx();
|
|
JS::Rooted<JSString*> jsCountryCode(
|
|
cx, JS_NewUCStringCopyZ(cx, countryCode.get()));
|
|
|
|
JS::Rooted<JS::Value> val(cx, JS::StringValue(jsCountryCode));
|
|
capturedPromise->MaybeResolve(val);
|
|
},
|
|
[requestHolder, capturedPromise](const nsresult rv) {
|
|
requestHolder->Complete();
|
|
// Resolve with null when countryCode is not available from the
|
|
// system
|
|
capturedPromise->MaybeResolve(JS::NullHandleValue);
|
|
})
|
|
->Track(*requestHolder);
|
|
|
|
promise.forget(aResult);
|
|
#endif
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsSystemInfo::GetProcessInfo(JSContext* aCx, Promise** aResult) {
|
|
NS_ENSURE_ARG_POINTER(aResult);
|
|
*aResult = nullptr;
|
|
|
|
if (!XRE_IsParentProcess()) {
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
|
|
nsIGlobalObject* global = xpc::CurrentNativeGlobal(aCx);
|
|
if (NS_WARN_IF(!global)) {
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
|
|
ErrorResult erv;
|
|
RefPtr<Promise> promise = Promise::Create(global, erv);
|
|
if (NS_WARN_IF(erv.Failed())) {
|
|
return erv.StealNSResult();
|
|
}
|
|
|
|
if (!mProcessInfoPromise) {
|
|
RefPtr<nsISerialEventTarget> backgroundET = GetBackgroundTarget();
|
|
|
|
mProcessInfoPromise = InvokeAsync(backgroundET, __func__, []() {
|
|
ProcessInfo info;
|
|
nsresult rv = CollectProcessInfo(info);
|
|
if (NS_SUCCEEDED(rv)) {
|
|
return ProcessInfoPromise::CreateAndResolve(info, __func__);
|
|
}
|
|
return ProcessInfoPromise::CreateAndReject(rv, __func__);
|
|
});
|
|
};
|
|
|
|
auto requestHolder =
|
|
MakeRefPtr<dom::DOMMozPromiseRequestHolder<ProcessInfoPromise>>(global);
|
|
|
|
// Chain the new promise to the extant mozpromise
|
|
RefPtr<Promise> capturedPromise = promise;
|
|
mProcessInfoPromise
|
|
->Then(
|
|
GetMainThreadSerialEventTarget(), __func__,
|
|
[requestHolder, capturedPromise](const ProcessInfo& info) {
|
|
requestHolder->Complete();
|
|
AutoJSAPI jsapi;
|
|
if (NS_WARN_IF(!jsapi.Init(capturedPromise->GetGlobalObject()))) {
|
|
capturedPromise->MaybeReject(NS_ERROR_UNEXPECTED);
|
|
return;
|
|
}
|
|
JSContext* cx = jsapi.cx();
|
|
JS::Rooted<JS::Value> val(
|
|
cx, JS::ObjectValue(*GetJSObjForProcessInfo(cx, info)));
|
|
capturedPromise->MaybeResolve(val);
|
|
},
|
|
[requestHolder, capturedPromise](const nsresult rv) {
|
|
requestHolder->Complete();
|
|
// Resolve with null when installYear is not available from the
|
|
// system
|
|
capturedPromise->MaybeResolve(JS::NullHandleValue);
|
|
})
|
|
->Track(*requestHolder);
|
|
|
|
promise.forget(aResult);
|
|
|
|
return NS_OK;
|
|
}
|