/* -*- 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/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"
#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
"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
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) {
info.cpuVendor.Assign(hw_impl.name);
for (
auto* p = &hw_impl.parts[0]; p->id != -1; ++p) {
if (p->id == (
int)cpuModel) {
info.cpuName.Assign(p->name);
}
}
}
}
# else
// cpuVendor from "vendor_id"
info.cpuVendor.Assign(keyValuePairs[
"vendor_id"_ns]);
// cpuName from "model name"
info.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();
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();
#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());
}
else {
info.cpuPCount = physicalCPUs;
info.cpuMCount = 0;
info.cpuECount = 0;
}
if (cpuSpeed >= 0) {
info.cpuSpeed = cpuSpeed;
}
else {
info.cpuSpeed = 0;
}
if (!cpuVendor.IsEmpty()) {
info.cpuVendor = cpuVendor;
}
if (!cpuName.IsEmpty()) {
info.cpuName = cpuName;
}
if (cpuFamily >= 0) {
info.cpuFamily = cpuFamily;
}
if (cpuModel >= 0) {
info.cpuModel = cpuModel;
}
if (cpuStepping >= 0) {
info.cpuStepping = cpuStepping;
}
if (logicalCPUs >= 0) {
info.cpuCount = logicalCPUs;
}
if (physicalCPUs >= 0) {
info.cpuCores = physicalCPUs;
}
if (cacheSizeL2 >= 0) {
info.l2cacheKB = cacheSizeL2;
}
if (cacheSizeL3 >= 0) {
info.l3cacheKB = 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
{
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__);
});
};
// Chain the new promise to the extant mozpromise
RefPtr<Promise> capturedPromise = promise;
mOSInfoPromise->Then(
GetMainThreadSerialEventTarget(), __func__,
[capturedPromise](
const OSInfo& info) {
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);
},
[capturedPromise](
const nsresult rv) {
// Resolve with null when installYear is not available from the system
capturedPromise->MaybeResolve(JS::NullHandleValue);
});
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__);
});
}
// Chain the new promise to the extant mozpromise.
RefPtr<Promise> capturedPromise = promise;
mDiskInfoPromise->Then(
GetMainThreadSerialEventTarget(), __func__,
[capturedPromise](
const DiskInfo& info) {
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;
}
JS::Rooted<JS::Value> val(cx, JS::ObjectValue(*jsInfo));
capturedPromise->MaybeResolve(val);
},
[capturedPromise](
const nsresult rv) {
capturedPromise->MaybeReject(rv);
});
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
--> --------------------
--> maximum size reached
--> --------------------