1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "mozilla/ArrayUtils.h"
9 #include "nsAppRunner.h"
10 #include "nsSystemInfo.h"
13 #include "mozilla/SSE.h"
14 #include "mozilla/arm.h"
15 #include "mozilla/Hal.h"
16 #include "mozilla/LazyIdleThread.h"
17 #include "mozilla/LookAndFeel.h"
18 #include "mozilla/Sprintf.h"
19 #include "mozilla/Try.h"
20 #include "mozilla/Vector.h"
22 #include "js/PropertyAndElement.h" // JS_SetProperty
23 #include "mozilla/dom/Promise.h"
30 # endif // __MINGW32__
32 # include <winioctl.h>
36 # endif // __MINGW32__
37 # include "base/scoped_handle_win.h"
38 # include "mozilla/DynamicallyLinkedFunctionPtr.h"
39 # include "mozilla/WindowsVersion.h"
40 # include "nsAppDirectoryServiceDefs.h"
41 # include "nsDirectoryServiceDefs.h"
42 # include "nsDirectoryServiceUtils.h"
43 # include "nsWindowsHelpers.h"
44 # include "WinUtils.h"
45 # include "mozilla/NotNull.h"
50 # include "MacHelpers.h"
56 # include "mozilla/WidgetUtilsGtk.h"
59 #if defined(XP_LINUX) && !defined(ANDROID)
62 # include "mozilla/Tokenizer.h"
63 # include "mozilla/widget/LSBUtils.h"
64 # include "nsCharSeparatedTokenizer.h"
70 #ifdef MOZ_WIDGET_ANDROID
71 # include "AndroidBuild.h"
72 # include "mozilla/java/GeckoAppShellWrappers.h"
73 # include "mozilla/jni/Utils.h"
77 # include <sys/sysctl.h>
80 #if defined(XP_LINUX) && defined(MOZ_SANDBOX)
81 # include "mozilla/SandboxInfo.h"
84 // Slot for NS_InitXPCOM to pass information to nsSystemInfo::Init.
85 // Only set to nonzero (potentially) if XP_UNIX. On such systems, the
86 // system call to discover the appropriate value is not thread-safe,
87 // so we must call it before going multithreaded, but nsSystemInfo::Init
88 // only happens well after that point.
89 uint32_t nsSystemInfo::gUserUmask
= 0;
91 using namespace mozilla::dom
;
94 # define RuntimeClass_Windows_System_Profile_WindowsIntegrityPolicy \
95 L"Windows.System.Profile.WindowsIntegrityPolicy"
97 using namespace Microsoft::WRL
;
98 using namespace Microsoft::WRL::Wrappers
;
99 using namespace ABI::Windows::Foundation
;
100 # endif // __MINGW32__
103 #if defined(XP_LINUX) && !defined(ANDROID)
104 static void SimpleParseKeyValuePairs(
105 const std::string
& aFilename
,
106 std::map
<nsCString
, nsCString
>& aKeyValuePairs
) {
107 std::ifstream
input(aFilename
.c_str());
108 for (std::string line
; std::getline(input
, line
);) {
109 nsAutoCString key
, value
;
111 nsCCharSeparatedTokenizer
tokens(nsDependentCString(line
.c_str()), ':');
112 if (tokens
.hasMoreTokens()) {
113 key
= tokens
.nextToken();
114 if (tokens
.hasMoreTokens()) {
115 value
= tokens
.nextToken();
117 // We want the value even if there was just one token, to cover the
118 // case where we had the key, and the value was blank (seems to be
119 // a valid scenario some files.)
120 aKeyValuePairs
[key
] = value
;
127 // Lifted from media/webrtc/trunk/webrtc/base/systeminfo.cc,
128 // so keeping the _ instead of switching to camel case for now.
129 static void GetProcessorInformation(int* physical_cpus
, int* cache_size_L2
,
130 int* cache_size_L3
) {
131 MOZ_ASSERT(physical_cpus
&& cache_size_L2
&& cache_size_L3
);
134 *cache_size_L2
= 0; // This will be in kbytes
135 *cache_size_L3
= 0; // This will be in kbytes
137 // Determine buffer size, allocate and get processor information.
138 // Size can change between calls (unlikely), so a loop is done.
139 SYSTEM_LOGICAL_PROCESSOR_INFORMATION info_buffer
[32];
140 SYSTEM_LOGICAL_PROCESSOR_INFORMATION
* infos
= &info_buffer
[0];
141 DWORD return_length
= sizeof(info_buffer
);
142 while (!::GetLogicalProcessorInformation(infos
, &return_length
)) {
143 if (GetLastError() == ERROR_INSUFFICIENT_BUFFER
&&
144 infos
== &info_buffer
[0]) {
145 infos
= new SYSTEM_LOGICAL_PROCESSOR_INFORMATION
146 [return_length
/ sizeof(SYSTEM_LOGICAL_PROCESSOR_INFORMATION
)];
153 i
< return_length
/ sizeof(SYSTEM_LOGICAL_PROCESSOR_INFORMATION
); ++i
) {
154 if (infos
[i
].Relationship
== RelationProcessorCore
) {
156 } else if (infos
[i
].Relationship
== RelationCache
) {
157 // Only care about L2 and L3 cache
158 switch (infos
[i
].Cache
.Level
) {
160 *cache_size_L2
= static_cast<int>(infos
[i
].Cache
.Size
/ 1024);
163 *cache_size_L3
= static_cast<int>(infos
[i
].Cache
.Size
/ 1024);
170 if (infos
!= &info_buffer
[0]) {
179 static nsresult
GetFolderDiskInfo(nsIFile
* file
, FolderDiskInfo
& info
) {
180 info
.model
.Truncate();
181 info
.revision
.Truncate();
184 nsAutoString filePath
;
185 nsresult rv
= file
->GetPath(filePath
);
186 NS_ENSURE_SUCCESS(rv
, rv
);
187 wchar_t volumeMountPoint
[MAX_PATH
] = {L
'\\', L
'\\', L
'.', L
'\\'};
188 const size_t PREFIX_LEN
= 4;
189 if (!::GetVolumePathNameW(filePath
.get(), volumeMountPoint
+ PREFIX_LEN
,
190 std::size(volumeMountPoint
) - PREFIX_LEN
)) {
191 return NS_ERROR_UNEXPECTED
;
193 size_t volumeMountPointLen
= wcslen(volumeMountPoint
);
194 // Since we would like to open a drive and not a directory, we need to
195 // remove any trailing backslash. A drive handle is valid for
196 // DeviceIoControl calls, a directory handle is not.
197 if (volumeMountPoint
[volumeMountPointLen
- 1] == L
'\\') {
198 volumeMountPoint
[volumeMountPointLen
- 1] = L
'\0';
200 ScopedHandle
handle(::CreateFileW(volumeMountPoint
, 0,
201 FILE_SHARE_READ
| FILE_SHARE_WRITE
, nullptr,
202 OPEN_EXISTING
, 0, nullptr));
203 if (!handle
.IsValid()) {
204 return NS_ERROR_UNEXPECTED
;
206 STORAGE_PROPERTY_QUERY queryParameters
= {StorageDeviceProperty
,
207 PropertyStandardQuery
};
208 STORAGE_DEVICE_DESCRIPTOR outputHeader
= {sizeof(STORAGE_DEVICE_DESCRIPTOR
)};
210 if (!::DeviceIoControl(handle
, IOCTL_STORAGE_QUERY_PROPERTY
, &queryParameters
,
211 sizeof(queryParameters
), &outputHeader
,
212 sizeof(outputHeader
), &bytesRead
, nullptr)) {
213 return NS_ERROR_FAILURE
;
215 PSTORAGE_DEVICE_DESCRIPTOR deviceOutput
=
216 (PSTORAGE_DEVICE_DESCRIPTOR
)malloc(outputHeader
.Size
);
217 if (!::DeviceIoControl(handle
, IOCTL_STORAGE_QUERY_PROPERTY
, &queryParameters
,
218 sizeof(queryParameters
), deviceOutput
,
219 outputHeader
.Size
, &bytesRead
, nullptr)) {
221 return NS_ERROR_FAILURE
;
224 queryParameters
.PropertyId
= StorageDeviceTrimProperty
;
227 DEVICE_TRIM_DESCRIPTOR trimDescriptor
= {sizeof(DEVICE_TRIM_DESCRIPTOR
)};
228 if (::DeviceIoControl(handle
, IOCTL_STORAGE_QUERY_PROPERTY
, &queryParameters
,
229 sizeof(queryParameters
), &trimDescriptor
,
230 sizeof(trimDescriptor
), &bytesRead
, nullptr)) {
231 if (trimDescriptor
.TrimEnabled
) {
238 queryParameters
.PropertyId
= StorageDeviceSeekPenaltyProperty
;
240 DEVICE_SEEK_PENALTY_DESCRIPTOR seekPenaltyDescriptor
= {
241 sizeof(DEVICE_SEEK_PENALTY_DESCRIPTOR
)};
242 if (::DeviceIoControl(handle
, IOCTL_STORAGE_QUERY_PROPERTY
,
243 &queryParameters
, sizeof(queryParameters
),
244 &seekPenaltyDescriptor
, sizeof(seekPenaltyDescriptor
),
245 &bytesRead
, nullptr)) {
246 // It is possible that the disk has TrimEnabled, but also
247 // IncursSeekPenalty; In this case, this is an HDD
248 if (seekPenaltyDescriptor
.IncursSeekPenalty
) {
254 // Some HDDs are including product ID info in the vendor field. Since PNP
255 // IDs include vendor info and product ID concatenated together, we'll do
256 // that here and interpret the result as a unique ID for the HDD model.
257 if (deviceOutput
->VendorIdOffset
) {
259 reinterpret_cast<char*>(deviceOutput
) + deviceOutput
->VendorIdOffset
;
261 if (deviceOutput
->ProductIdOffset
) {
263 reinterpret_cast<char*>(deviceOutput
) + deviceOutput
->ProductIdOffset
;
265 info
.model
.CompressWhitespace();
266 if (deviceOutput
->ProductRevisionOffset
) {
267 info
.revision
= reinterpret_cast<char*>(deviceOutput
) +
268 deviceOutput
->ProductRevisionOffset
;
269 info
.revision
.CompressWhitespace();
276 static nsresult
CollectDiskInfo(nsIFile
* greDir
, nsIFile
* winDir
,
277 nsIFile
* profDir
, DiskInfo
& info
) {
278 nsresult rv
= GetFolderDiskInfo(greDir
, info
.binary
);
282 rv
= GetFolderDiskInfo(winDir
, info
.system
);
286 return GetFolderDiskInfo(profDir
, info
.profile
);
289 static nsresult
CollectOSInfo(OSInfo
& info
) {
290 HKEY installYearHKey
;
291 LONG status
= RegOpenKeyExW(
292 HKEY_LOCAL_MACHINE
, L
"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion", 0,
293 KEY_READ
| KEY_WOW64_64KEY
, &installYearHKey
);
295 if (status
!= ERROR_SUCCESS
) {
296 return NS_ERROR_UNEXPECTED
;
299 nsAutoRegKey
installYearKey(installYearHKey
);
303 DWORD time_size
= sizeof(time_t);
305 status
= RegQueryValueExW(installYearHKey
, L
"InstallDate", nullptr, &type
,
306 (LPBYTE
)&raw_time
, &time_size
);
308 if (status
!= ERROR_SUCCESS
) {
309 return NS_ERROR_UNEXPECTED
;
312 if (type
!= REG_DWORD
) {
313 return NS_ERROR_UNEXPECTED
;
317 if (localtime_s(&time
, &raw_time
) != 0) {
318 return NS_ERROR_UNEXPECTED
;
321 info
.installYear
= 1900UL + time
.tm_year
;
323 nsAutoServiceHandle
scm(
324 OpenSCManager(nullptr, SERVICES_ACTIVE_DATABASE
, SC_MANAGER_CONNECT
));
327 return NS_ERROR_UNEXPECTED
;
330 bool superfetchServiceRunning
= false;
332 // Superfetch was introduced in Windows Vista as a service with the name
333 // SysMain. The service display name was also renamed to SysMain after Windows
335 nsAutoServiceHandle
hService(OpenService(scm
, L
"SysMain", GENERIC_READ
));
338 SERVICE_STATUS superfetchStatus
;
339 LPSERVICE_STATUS pSuperfetchStatus
= &superfetchStatus
;
341 if (!QueryServiceStatus(hService
, pSuperfetchStatus
)) {
342 return NS_ERROR_UNEXPECTED
;
345 superfetchServiceRunning
=
346 superfetchStatus
.dwCurrentState
== SERVICE_RUNNING
;
349 // If the SysMain (Superfetch) service is available, but not configured using
350 // the defaults, then it's disabled for our purposes, since it's not going to
351 // be operating as expected.
352 bool superfetchUsingDefaultParams
= true;
353 bool prefetchUsingDefaultParams
= true;
355 static const WCHAR prefetchParamsKeyName
[] =
356 L
"SYSTEM\\CurrentControlSet\\Control\\Session Manager\\Memory "
357 L
"Management\\PrefetchParameters";
358 static const DWORD SUPERFETCH_DEFAULT_PARAM
= 3;
359 static const DWORD PREFETCH_DEFAULT_PARAM
= 3;
361 HKEY prefetchParamsHKey
;
363 LONG prefetchParamsStatus
=
364 RegOpenKeyExW(HKEY_LOCAL_MACHINE
, prefetchParamsKeyName
, 0,
365 KEY_READ
| KEY_WOW64_64KEY
, &prefetchParamsHKey
);
367 if (prefetchParamsStatus
== ERROR_SUCCESS
) {
368 DWORD valueSize
= sizeof(DWORD
);
369 DWORD superfetchValue
= 0;
370 nsAutoRegKey
prefetchParamsKey(prefetchParamsHKey
);
371 LONG superfetchParamStatus
= RegQueryValueExW(
372 prefetchParamsHKey
, L
"EnableSuperfetch", nullptr, &type
,
373 reinterpret_cast<LPBYTE
>(&superfetchValue
), &valueSize
);
375 // If the EnableSuperfetch registry key doesn't exist, then it's using the
376 // default configuration.
377 if (superfetchParamStatus
== ERROR_SUCCESS
&&
378 superfetchValue
!= SUPERFETCH_DEFAULT_PARAM
) {
379 superfetchUsingDefaultParams
= false;
382 DWORD prefetchValue
= 0;
384 LONG prefetchParamStatus
= RegQueryValueExW(
385 prefetchParamsHKey
, L
"EnablePrefetcher", nullptr, &type
,
386 reinterpret_cast<LPBYTE
>(&prefetchValue
), &valueSize
);
388 // If the EnablePrefetcher registry key doesn't exist, then we interpret
389 // that as the Prefetcher being disabled (since Prefetch behaviour when
390 // the key is not available appears to be undefined).
391 if (prefetchParamStatus
!= ERROR_SUCCESS
||
392 prefetchValue
!= PREFETCH_DEFAULT_PARAM
) {
393 prefetchUsingDefaultParams
= false;
397 info
.hasSuperfetch
= superfetchServiceRunning
&& superfetchUsingDefaultParams
;
398 info
.hasPrefetch
= prefetchUsingDefaultParams
;
403 nsresult
CollectCountryCode(nsAString
& aCountryCode
) {
404 GEOID geoid
= GetUserGeoID(GEOCLASS_NATION
);
405 if (geoid
== GEOID_NOT_AVAILABLE
) {
406 return NS_ERROR_NOT_AVAILABLE
;
408 // Get required length
409 int numChars
= GetGeoInfoW(geoid
, GEO_ISO2
, nullptr, 0, 0);
411 return NS_ERROR_FAILURE
;
413 // Now get the string for real
414 aCountryCode
.SetLength(numChars
);
416 GetGeoInfoW(geoid
, GEO_ISO2
, char16ptr_t(aCountryCode
.BeginWriting()),
417 aCountryCode
.Length(), 0);
419 return NS_ERROR_FAILURE
;
422 // numChars includes null terminator
423 aCountryCode
.Truncate(numChars
- 1);
431 static HRESULT
EnumWSCProductList(
432 nsAString
& aOutput
, mozilla::NotNull
<IWSCProductList
*> aProdList
) {
433 MOZ_ASSERT(aOutput
.IsEmpty());
436 HRESULT hr
= aProdList
->get_Count(&count
);
441 for (LONG index
= 0; index
< count
; ++index
) {
442 RefPtr
<IWscProduct
> product
;
443 hr
= aProdList
->get_Item(index
, getter_AddRefs(product
));
448 WSC_SECURITY_PRODUCT_STATE state
;
449 hr
= product
->get_ProductState(&state
);
454 // We only care about products that are active
455 if (state
== WSC_SECURITY_PRODUCT_STATE_OFF
||
456 state
== WSC_SECURITY_PRODUCT_STATE_SNOOZED
) {
461 hr
= product
->get_ProductName(bName
.GetAddress());
466 if (!aOutput
.IsEmpty()) {
467 aOutput
.AppendLiteral(u
";");
470 aOutput
.Append((wchar_t*)bName
, bName
.length());
476 static nsresult
GetWindowsSecurityCenterInfo(nsAString
& aAVInfo
,
477 nsAString
& aAntiSpyInfo
,
478 nsAString
& aFirewallInfo
) {
480 aAntiSpyInfo
.Truncate();
481 aFirewallInfo
.Truncate();
483 if (!XRE_IsParentProcess()) {
484 return NS_ERROR_NOT_AVAILABLE
;
487 const CLSID clsid
= __uuidof(WSCProductList
);
488 const IID iid
= __uuidof(IWSCProductList
);
490 // NB: A separate instance of IWSCProductList is needed for each distinct
491 // security provider type; MSDN says that we cannot reuse the same object
492 // and call Initialize() to pave over the previous data.
494 WSC_SECURITY_PROVIDER providerTypes
[] = {WSC_SECURITY_PROVIDER_ANTIVIRUS
,
495 WSC_SECURITY_PROVIDER_ANTISPYWARE
,
496 WSC_SECURITY_PROVIDER_FIREWALL
};
498 // Each output must match the corresponding entry in providerTypes.
499 nsAString
* outputs
[] = {&aAVInfo
, &aAntiSpyInfo
, &aFirewallInfo
};
501 static_assert(std::size(providerTypes
) == std::size(outputs
),
502 "Length of providerTypes and outputs arrays must match");
504 for (uint32_t index
= 0; index
< std::size(providerTypes
); ++index
) {
505 RefPtr
<IWSCProductList
> prodList
;
506 HRESULT hr
= ::CoCreateInstance(clsid
, nullptr, CLSCTX_INPROC_SERVER
, iid
,
507 getter_AddRefs(prodList
));
509 return NS_ERROR_NOT_AVAILABLE
;
512 hr
= prodList
->Initialize(providerTypes
[index
]);
514 return NS_ERROR_UNEXPECTED
;
517 hr
= EnumWSCProductList(*outputs
[index
],
518 mozilla::WrapNotNull(prodList
.get()));
520 return NS_ERROR_UNEXPECTED
;
527 # endif // __MINGW32__
529 #endif // defined(XP_WIN)
532 static nsresult
GetAppleModelId(nsAutoCString
& aModelId
) {
534 size_t result
= sysctlbyname("hw.model", nullptr, &numChars
, nullptr, 0);
535 if (result
!= 0 || !numChars
) {
536 return NS_ERROR_FAILURE
;
538 aModelId
.SetLength(numChars
);
540 sysctlbyname("hw.model", aModelId
.BeginWriting(), &numChars
, nullptr, 0);
542 return NS_ERROR_FAILURE
;
544 // numChars includes null terminator
545 aModelId
.Truncate(numChars
- 1);
549 static nsresult
ProcessIsRosettaTranslated(bool& isRosetta
) {
550 # if defined(__aarch64__)
551 // There is no need to call sysctlbyname() if we are running as arm64.
555 size_t size
= sizeof(ret
);
556 if (sysctlbyname("sysctl.proc_translated", &ret
, &size
, NULL
, 0) == -1) {
557 if (errno
!= ENOENT
) {
558 fprintf(stderr
, "Failed to check for translation environment\n");
562 isRosetta
= (ret
== 1);
569 using namespace mozilla
;
571 nsSystemInfo::nsSystemInfo() = default;
573 nsSystemInfo::~nsSystemInfo() = default;
575 // CPU-specific information.
576 static const struct PropItems
{
578 bool (*propfun
)(void);
580 // x86-specific bits.
581 {"hasMMX", mozilla::supports_mmx
},
582 {"hasSSE", mozilla::supports_sse
},
583 {"hasSSE2", mozilla::supports_sse2
},
584 {"hasSSE3", mozilla::supports_sse3
},
585 {"hasSSSE3", mozilla::supports_ssse3
},
586 {"hasSSE4A", mozilla::supports_sse4a
},
587 {"hasSSE4_1", mozilla::supports_sse4_1
},
588 {"hasSSE4_2", mozilla::supports_sse4_2
},
589 {"hasAVX", mozilla::supports_avx
},
590 {"hasAVX2", mozilla::supports_avx2
},
591 {"hasAES", mozilla::supports_aes
},
592 // ARM-specific bits.
593 {"hasEDSP", mozilla::supports_edsp
},
594 {"hasARMv6", mozilla::supports_armv6
},
595 {"hasARMv7", mozilla::supports_armv7
},
596 {"hasNEON", mozilla::supports_neon
}};
598 nsresult
CollectProcessInfo(ProcessInfo
& info
) {
599 nsAutoCString cpuVendor
;
600 nsAutoCString cpuName
;
604 int cpuStepping
= -1;
605 int logicalCPUs
= -1;
606 int physicalCPUs
= -1;
607 int cacheSizeL2
= -1;
608 int cacheSizeL3
= -1;
611 // IsWow64Process2 is only available on Windows 10+, so we have to dynamically
612 // check for its existence.
613 typedef BOOL(WINAPI
* LPFN_IWP2
)(HANDLE
, USHORT
*, USHORT
*);
614 LPFN_IWP2 iwp2
= reinterpret_cast<LPFN_IWP2
>(
615 GetProcAddress(GetModuleHandle(L
"kernel32"), "IsWow64Process2"));
616 BOOL isWow64
= FALSE
;
617 USHORT processMachine
= IMAGE_FILE_MACHINE_UNKNOWN
;
618 USHORT nativeMachine
= IMAGE_FILE_MACHINE_UNKNOWN
;
621 gotWow64Value
= iwp2(GetCurrentProcess(), &processMachine
, &nativeMachine
);
623 isWow64
= (processMachine
!= IMAGE_FILE_MACHINE_UNKNOWN
);
626 gotWow64Value
= IsWow64Process(GetCurrentProcess(), &isWow64
);
627 // The function only indicates a WOW64 environment if it's 32-bit x86
628 // running on x86-64, so emulate what IsWow64Process2 would have given.
629 if (gotWow64Value
&& isWow64
) {
630 processMachine
= IMAGE_FILE_MACHINE_I386
;
631 nativeMachine
= IMAGE_FILE_MACHINE_AMD64
;
634 NS_WARNING_ASSERTION(gotWow64Value
, "IsWow64Process failed");
636 // Set this always, even for the x86-on-arm64 case.
637 info
.isWow64
= !!isWow64
;
638 // Additional information if we're running x86-on-arm64
639 info
.isWowARM64
= (processMachine
== IMAGE_FILE_MACHINE_I386
&&
640 nativeMachine
== IMAGE_FILE_MACHINE_ARM64
);
646 // WindowsIntegrityPolicy is only available on newer versions
647 // of Windows 10, so there's no point in trying to check this
648 // on earlier versions. We know GetActivationFactory crashes on
649 // Windows 7 when trying to retrieve this class, and may also
650 // crash on very old versions of Windows 10.
651 if (IsWin10Sep2018UpdateOrLater()) {
652 ComPtr
<IWindowsIntegrityPolicyStatics
> wip
;
653 HRESULT hr
= GetActivationFactory(
655 RuntimeClass_Windows_System_Profile_WindowsIntegrityPolicy
)
659 // info.isWindowsSMode ends up true if Windows is in S mode, otherwise
661 // https://docs.microsoft.com/en-us/uwp/api/windows.system.profile.windowsintegritypolicy.isenabled?view=winrt-22000
662 hr
= wip
->get_IsEnabled(&info
.isWindowsSMode
);
663 NS_WARNING_ASSERTION(SUCCEEDED(hr
),
664 "WindowsIntegrityPolicy.IsEnabled failed");
667 # endif // __MINGW32__
671 static const WCHAR keyName
[] =
672 L
"HARDWARE\\DESCRIPTION\\System\\CentralProcessor\\0";
674 if (RegOpenKeyEx(HKEY_LOCAL_MACHINE
, keyName
, 0, KEY_QUERY_VALUE
, &key
) ==
676 DWORD data
, len
, vtype
;
679 if (RegQueryValueEx(key
, L
"~Mhz", 0, 0, reinterpret_cast<LPBYTE
>(&data
),
680 &len
) == ERROR_SUCCESS
) {
681 cpuSpeed
= static_cast<int>(data
);
684 // Limit to 64 double byte characters, should be plenty, but create
685 // a buffer one larger as the result may not be null terminated. If
686 // it is more than 64, we will not get the value.
687 wchar_t cpuVendorStr
[64 + 1];
688 len
= sizeof(cpuVendorStr
) - 2;
689 if (RegQueryValueExW(key
, L
"VendorIdentifier", 0, &vtype
,
690 reinterpret_cast<LPBYTE
>(cpuVendorStr
),
691 &len
) == ERROR_SUCCESS
&&
692 vtype
== REG_SZ
&& len
% 2 == 0 && len
> 1) {
693 cpuVendorStr
[len
/ 2] = 0; // In case it isn't null terminated
694 CopyUTF16toUTF8(nsDependentString(cpuVendorStr
), cpuVendor
);
697 // Limit to 64 double byte characters, should be plenty, but create
698 // a buffer one larger as the result may not be null terminated. If
699 // it is more than 64, we will not get the value.
700 // The expected string size is 48 characters or less.
701 wchar_t cpuNameStr
[64 + 1];
702 len
= sizeof(cpuNameStr
) - 2;
703 if (RegQueryValueExW(key
, L
"ProcessorNameString", 0, &vtype
,
704 reinterpret_cast<LPBYTE
>(cpuNameStr
),
705 &len
) == ERROR_SUCCESS
&&
706 vtype
== REG_SZ
&& len
% 2 == 0 && len
> 1) {
707 cpuNameStr
[len
/ 2] = 0; // In case it isn't null terminated
708 CopyUTF16toUTF8(nsDependentString(cpuNameStr
), cpuName
);
714 // Other CPU attributes:
716 GetNativeSystemInfo(&si
);
717 logicalCPUs
= si
.dwNumberOfProcessors
;
718 GetProcessorInformation(&physicalCPUs
, &cacheSizeL2
, &cacheSizeL3
);
719 if (physicalCPUs
<= 0) {
720 physicalCPUs
= logicalCPUs
;
722 cpuFamily
= si
.wProcessorLevel
;
723 cpuModel
= si
.wProcessorRevision
>> 8;
724 cpuStepping
= si
.wProcessorRevision
& 0xFF;
725 #elif defined(XP_MACOSX)
727 uint64_t sysctlValue64
= 0;
728 uint32_t sysctlValue32
= 0;
730 len
= sizeof(sysctlValue64
);
731 if (!sysctlbyname("hw.cpufrequency_max", &sysctlValue64
, &len
, NULL
, 0)) {
732 cpuSpeed
= static_cast<int>(sysctlValue64
/ 1000000);
734 MOZ_ASSERT(sizeof(sysctlValue64
) == len
);
736 len
= sizeof(sysctlValue32
);
737 if (!sysctlbyname("hw.physicalcpu_max", &sysctlValue32
, &len
, NULL
, 0)) {
738 physicalCPUs
= static_cast<int>(sysctlValue32
);
740 MOZ_ASSERT(sizeof(sysctlValue32
) == len
);
742 len
= sizeof(sysctlValue32
);
743 if (!sysctlbyname("hw.logicalcpu_max", &sysctlValue32
, &len
, NULL
, 0)) {
744 logicalCPUs
= static_cast<int>(sysctlValue32
);
746 MOZ_ASSERT(sizeof(sysctlValue32
) == len
);
748 len
= sizeof(sysctlValue64
);
749 if (!sysctlbyname("hw.l2cachesize", &sysctlValue64
, &len
, NULL
, 0)) {
750 cacheSizeL2
= static_cast<int>(sysctlValue64
/ 1024);
752 MOZ_ASSERT(sizeof(sysctlValue64
) == len
);
754 len
= sizeof(sysctlValue64
);
755 if (!sysctlbyname("hw.l3cachesize", &sysctlValue64
, &len
, NULL
, 0)) {
756 cacheSizeL3
= static_cast<int>(sysctlValue64
/ 1024);
758 MOZ_ASSERT(sizeof(sysctlValue64
) == len
);
760 if (!sysctlbyname("machdep.cpu.vendor", NULL
, &len
, NULL
, 0)) {
761 char* cpuVendorStr
= new char[len
];
762 if (!sysctlbyname("machdep.cpu.vendor", cpuVendorStr
, &len
, NULL
, 0)) {
763 cpuVendor
= cpuVendorStr
;
765 delete[] cpuVendorStr
;
768 if (!sysctlbyname("machdep.cpu.brand_string", NULL
, &len
, NULL
, 0)) {
769 char* cpuNameStr
= new char[len
];
770 if (!sysctlbyname("machdep.cpu.brand_string", cpuNameStr
, &len
, NULL
, 0)) {
771 cpuName
= cpuNameStr
;
776 len
= sizeof(sysctlValue32
);
777 if (!sysctlbyname("machdep.cpu.family", &sysctlValue32
, &len
, NULL
, 0)) {
778 cpuFamily
= static_cast<int>(sysctlValue32
);
780 MOZ_ASSERT(sizeof(sysctlValue32
) == len
);
782 len
= sizeof(sysctlValue32
);
783 if (!sysctlbyname("machdep.cpu.model", &sysctlValue32
, &len
, NULL
, 0)) {
784 cpuModel
= static_cast<int>(sysctlValue32
);
786 MOZ_ASSERT(sizeof(sysctlValue32
) == len
);
788 len
= sizeof(sysctlValue32
);
789 if (!sysctlbyname("machdep.cpu.stepping", &sysctlValue32
, &len
, NULL
, 0)) {
790 cpuStepping
= static_cast<int>(sysctlValue32
);
792 MOZ_ASSERT(sizeof(sysctlValue32
) == len
);
794 #elif defined(XP_LINUX) && !defined(ANDROID)
795 // Get vendor, family, model, stepping, physical cores
796 // from /proc/cpuinfo file
798 std::map
<nsCString
, nsCString
> keyValuePairs
;
799 SimpleParseKeyValuePairs("/proc/cpuinfo", keyValuePairs
);
801 # if defined(__arm__) || defined(__aarch64__)
802 // The tables below were taken from
803 // https://raw.githubusercontent.com/util-linux/util-linux/e3192bfd1dd129c70f5416e1464135d8cd22c956/sys-utils/lscpu-arm.c
805 /* clang-format off */
811 static const struct id_part arm_part
[] = {
819 { 0xa20, "ARM1020" },
820 { 0xa22, "ARM1022" },
821 { 0xa26, "ARM1026" },
822 { 0xb02, "ARM11 MPCore" },
823 { 0xb36, "ARM1136" },
824 { 0xb56, "ARM1156" },
825 { 0xb76, "ARM1176" },
826 { 0xc05, "Cortex-A5" },
827 { 0xc07, "Cortex-A7" },
828 { 0xc08, "Cortex-A8" },
829 { 0xc09, "Cortex-A9" },
830 { 0xc0d, "Cortex-A17" }, /* Originally A12 */
831 { 0xc0f, "Cortex-A15" },
832 { 0xc0e, "Cortex-A17" },
833 { 0xc14, "Cortex-R4" },
834 { 0xc15, "Cortex-R5" },
835 { 0xc17, "Cortex-R7" },
836 { 0xc18, "Cortex-R8" },
837 { 0xc20, "Cortex-M0" },
838 { 0xc21, "Cortex-M1" },
839 { 0xc23, "Cortex-M3" },
840 { 0xc24, "Cortex-M4" },
841 { 0xc27, "Cortex-M7" },
842 { 0xc60, "Cortex-M0+" },
843 { 0xd01, "Cortex-A32" },
844 { 0xd02, "Cortex-A34" },
845 { 0xd03, "Cortex-A53" },
846 { 0xd04, "Cortex-A35" },
847 { 0xd05, "Cortex-A55" },
848 { 0xd06, "Cortex-A65" },
849 { 0xd07, "Cortex-A57" },
850 { 0xd08, "Cortex-A72" },
851 { 0xd09, "Cortex-A73" },
852 { 0xd0a, "Cortex-A75" },
853 { 0xd0b, "Cortex-A76" },
854 { 0xd0c, "Neoverse-N1" },
855 { 0xd0d, "Cortex-A77" },
856 { 0xd0e, "Cortex-A76AE" },
857 { 0xd13, "Cortex-R52" },
858 { 0xd15, "Cortex-R82" },
859 { 0xd16, "Cortex-R52+" },
860 { 0xd20, "Cortex-M23" },
861 { 0xd21, "Cortex-M33" },
862 { 0xd22, "Cortex-M55" },
863 { 0xd23, "Cortex-M85" },
864 { 0xd40, "Neoverse-V1" },
865 { 0xd41, "Cortex-A78" },
866 { 0xd42, "Cortex-A78AE" },
867 { 0xd43, "Cortex-A65AE" },
868 { 0xd44, "Cortex-X1" },
869 { 0xd46, "Cortex-A510" },
870 { 0xd47, "Cortex-A710" },
871 { 0xd48, "Cortex-X2" },
872 { 0xd49, "Neoverse-N2" },
873 { 0xd4a, "Neoverse-E1" },
874 { 0xd4b, "Cortex-A78C" },
875 { 0xd4c, "Cortex-X1C" },
876 { 0xd4d, "Cortex-A715" },
877 { 0xd4e, "Cortex-X3" },
878 { 0xd4f, "Neoverse-V2" },
879 { 0xd80, "Cortex-A520" },
880 { 0xd81, "Cortex-A720" },
881 { 0xd82, "Cortex-X4" },
882 { 0xd84, "Neoverse-V3" },
883 { 0xd8e, "Neoverse-N3" },
887 static const struct id_part brcm_part
[] = {
888 { 0x0f, "Brahma-B15" },
889 { 0x100, "Brahma-B53" },
890 { 0x516, "ThunderX2" },
894 static const struct id_part dec_part
[] = {
900 static const struct id_part cavium_part
[] = {
901 { 0x0a0, "ThunderX" },
902 { 0x0a1, "ThunderX-88XX" },
903 { 0x0a2, "ThunderX-81XX" },
904 { 0x0a3, "ThunderX-83XX" },
905 { 0x0af, "ThunderX2-99xx" },
906 { 0x0b0, "OcteonTX2" },
907 { 0x0b1, "OcteonTX2-98XX" },
908 { 0x0b2, "OcteonTX2-96XX" },
909 { 0x0b3, "OcteonTX2-95XX" },
910 { 0x0b4, "OcteonTX2-95XXN" },
911 { 0x0b5, "OcteonTX2-95XXMM" },
912 { 0x0b6, "OcteonTX2-95XXO" },
913 { 0x0b8, "ThunderX3-T110" },
917 static const struct id_part apm_part
[] = {
922 static const struct id_part qcom_part
[] = {
923 { 0x00f, "Scorpion" },
924 { 0x02d, "Scorpion" },
930 { 0x800, "Falkor-V1/Kryo" },
931 { 0x801, "Kryo-V2" },
932 { 0x802, "Kryo-3XX-Gold" },
933 { 0x803, "Kryo-3XX-Silver" },
934 { 0x804, "Kryo-4XX-Gold" },
935 { 0x805, "Kryo-4XX-Silver" },
937 { 0xc01, "Saphira" },
941 static const struct id_part samsung_part
[] = {
942 { 0x001, "exynos-m1" },
943 { 0x002, "exynos-m3" },
944 { 0x003, "exynos-m4" },
945 { 0x004, "exynos-m5" },
949 static const struct id_part nvidia_part
[] = {
951 { 0x003, "Denver 2" },
956 static const struct id_part marvell_part
[] = {
957 { 0x131, "Feroceon-88FR131" },
958 { 0x581, "PJ4/PJ4b" },
959 { 0x584, "PJ4B-MP" },
963 static const struct id_part apple_part
[] = {
965 { 0x001, "Cyclone" },
966 { 0x002, "Typhoon" },
967 { 0x003, "Typhoon/Capri" },
968 { 0x004, "Twister" },
969 { 0x005, "Twister/Elba/Malta" },
970 { 0x006, "Hurricane" },
971 { 0x007, "Hurricane/Myst" },
972 { 0x008, "Monsoon" },
973 { 0x009, "Mistral" },
975 { 0x00c, "Tempest" },
976 { 0x00f, "Tempest-M9" },
977 { 0x010, "Vortex/Aruba" },
978 { 0x011, "Tempest/Aruba" },
979 { 0x012, "Lightning" },
980 { 0x013, "Thunder" },
981 { 0x020, "Icestorm-A14" },
982 { 0x021, "Firestorm-A14" },
983 { 0x022, "Icestorm-M1" },
984 { 0x023, "Firestorm-M1" },
985 { 0x024, "Icestorm-M1-Pro" },
986 { 0x025, "Firestorm-M1-Pro" },
987 { 0x026, "Thunder-M10" },
988 { 0x028, "Icestorm-M1-Max" },
989 { 0x029, "Firestorm-M1-Max" },
990 { 0x030, "Blizzard-A15" },
991 { 0x031, "Avalanche-A15" },
992 { 0x032, "Blizzard-M2" },
993 { 0x033, "Avalanche-M2" },
994 { 0x034, "Blizzard-M2-Pro" },
995 { 0x035, "Avalanche-M2-Pro" },
996 { 0x036, "Sawtooth-A16" },
997 { 0x037, "Everest-A16" },
998 { 0x038, "Blizzard-M2-Max" },
999 { 0x039, "Avalanche-M2-Max" },
1003 static const struct id_part faraday_part
[] = {
1009 static const struct id_part intel_part
[] = {
1010 { 0x200, "i80200" },
1011 { 0x210, "PXA250A" },
1012 { 0x212, "PXA210A" },
1013 { 0x242, "i80321-400" },
1014 { 0x243, "i80321-600" },
1015 { 0x290, "PXA250B/PXA26x" },
1016 { 0x292, "PXA210B" },
1017 { 0x2c2, "i80321-400-B0" },
1018 { 0x2c3, "i80321-600-B0" },
1019 { 0x2d0, "PXA250C/PXA255/PXA26x" },
1020 { 0x2d2, "PXA210C" },
1021 { 0x411, "PXA27x" },
1022 { 0x41c, "IPX425-533" },
1023 { 0x41d, "IPX425-400" },
1024 { 0x41f, "IPX425-266" },
1025 { 0x682, "PXA32x" },
1026 { 0x683, "PXA930/PXA935" },
1027 { 0x688, "PXA30x" },
1028 { 0x689, "PXA31x" },
1029 { 0xb11, "SA1110" },
1030 { 0xc12, "IPX1200" },
1034 static const struct id_part fujitsu_part
[] = {
1039 static const struct id_part hisi_part
[] = {
1040 { 0xd01, "TaiShan-v110" }, /* used in Kunpeng-920 SoC */
1041 { 0xd02, "TaiShan-v120" }, /* used in Kirin 990A and 9000S SoCs */
1042 { 0xd40, "Cortex-A76" }, /* HiSilicon uses this ID though advertises A76 */
1043 { 0xd41, "Cortex-A77" }, /* HiSilicon uses this ID though advertises A77 */
1047 static const struct id_part ampere_part
[] = {
1048 { 0xac3, "Ampere-1" },
1049 { 0xac4, "Ampere-1a" },
1053 static const struct id_part ft_part
[] = {
1054 { 0x303, "FTC310" },
1055 { 0x660, "FTC660" },
1056 { 0x661, "FTC661" },
1057 { 0x662, "FTC662" },
1058 { 0x663, "FTC663" },
1059 { 0x664, "FTC664" },
1060 { 0x862, "FTC862" },
1064 static const struct id_part ms_part
[] = {
1065 { 0xd49, "Azure-Cobalt-100" },
1069 static const struct id_part unknown_part
[] = {
1075 const struct id_part
*parts
;
1079 static const struct hw_impl hw_implementer
[] = {
1080 { 0x41, arm_part
, "ARM" },
1081 { 0x42, brcm_part
, "Broadcom" },
1082 { 0x43, cavium_part
, "Cavium" },
1083 { 0x44, dec_part
, "DEC" },
1084 { 0x46, fujitsu_part
, "FUJITSU" },
1085 { 0x48, hisi_part
, "HiSilicon" },
1086 { 0x49, unknown_part
, "Infineon" },
1087 { 0x4d, unknown_part
, "Motorola/Freescale" },
1088 { 0x4e, nvidia_part
, "NVIDIA" },
1089 { 0x50, apm_part
, "APM" },
1090 { 0x51, qcom_part
, "Qualcomm" },
1091 { 0x53, samsung_part
, "Samsung" },
1092 { 0x56, marvell_part
, "Marvell" },
1093 { 0x61, apple_part
, "Apple" },
1094 { 0x66, faraday_part
, "Faraday" },
1095 { 0x69, intel_part
, "Intel" },
1096 { 0x6d, ms_part
, "Microsoft" },
1097 { 0x70, ft_part
, "Phytium" },
1098 { 0xc0, ampere_part
, "Ampere" },
1099 { -1, unknown_part
, "unknown" },
1101 /* clang-format on */
1103 // cpuFamily from "CPU implementer". Technically, this is only the vendor,
1104 // but this is the closed to a family we can get.
1105 (void)Tokenizer(keyValuePairs
["CPU implementer"_ns
])
1106 .ReadHexadecimal(&cpuFamily
);
1108 // cpuModel from "CPU part". Not exactly a model number, but close enough,
1109 // and that's what lscpu uses.
1110 (void)Tokenizer(keyValuePairs
["CPU part"_ns
]).ReadHexadecimal(&cpuModel
);
1112 // cpuStepping from "CPU variant" (that's what lscpu uses).
1113 (void)Tokenizer(keyValuePairs
["CPU variant"_ns
])
1114 .ReadHexadecimal(&cpuStepping
);
1116 for (auto& hw_impl
: hw_implementer
) {
1117 if (hw_impl
.id
== (int)cpuFamily
) {
1118 info
.cpuVendor
.Assign(hw_impl
.name
);
1119 for (auto* p
= &hw_impl
.parts
[0]; p
->id
!= -1; ++p
) {
1120 if (p
->id
== (int)cpuModel
) {
1121 info
.cpuName
.Assign(p
->name
);
1127 // cpuVendor from "vendor_id"
1128 info
.cpuVendor
.Assign(keyValuePairs
["vendor_id"_ns
]);
1130 // cpuName from "model name"
1131 info
.cpuName
.Assign(keyValuePairs
["model name"_ns
]);
1133 // cpuFamily from "cpu family"
1134 (void)Tokenizer(keyValuePairs
["cpu family"_ns
]).ReadInteger(&cpuFamily
);
1136 // cpuModel from "model"
1137 (void)Tokenizer(keyValuePairs
["model"_ns
]).ReadInteger(&cpuModel
);
1139 // cpuStepping from "stepping"
1140 (void)Tokenizer(keyValuePairs
["stepping"_ns
]).ReadInteger(&cpuStepping
);
1145 // Get cpuSpeed from another file.
1146 std::ifstream
input(
1147 "/sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_max_freq");
1149 if (getline(input
, line
)) {
1150 (void)Tokenizer(line
.c_str()).ReadInteger(&cpuSpeed
);
1156 // Get cacheSizeL2 from yet another file
1157 std::ifstream
input("/sys/devices/system/cpu/cpu0/cache/index2/size");
1159 if (getline(input
, line
)) {
1160 (void)Tokenizer(line
.c_str(), nullptr, "K").ReadInteger(&cacheSizeL2
);
1165 // Get cacheSizeL3 from yet another file
1166 std::ifstream
input("/sys/devices/system/cpu/cpu0/cache/index3/size");
1168 if (getline(input
, line
)) {
1169 (void)Tokenizer(line
.c_str(), nullptr, "K").ReadInteger(&cacheSizeL3
);
1173 info
.cpuCount
= PR_GetNumberOfProcessors();
1174 int max_cpu_bits
= [&] {
1175 // PR_GetNumberOfProcessors gets the value from
1176 // /sys/devices/system/cpu/present, but the number of bits in the CPU masks
1177 // we're going to read below can be larger (for instance, on the 32-core
1178 // 64-threads Threadripper 3970X, PR_GetNumberOfProcessors returns 64, but
1179 // the number of bits in the CPU masks is 128). That number of bits is
1180 // correlated with the number of CPUs possible (which is different from the
1181 // number of CPUs present).
1182 std::ifstream
input("/sys/devices/system/cpu/possible");
1184 if (getline(input
, line
)) {
1186 Tokenizer
p(line
.c_str());
1187 // The expected format is `0-n` where n is the number of CPUs possible
1189 if (p
.ReadInteger(&num
) && num
== 0 && p
.CheckChar('-') &&
1190 p
.ReadInteger(&num
) && p
.CheckEOF()) {
1194 // If we weren't able to get the value from /sys/devices/system/cpu/possible
1195 // from some reason, fallback to cpuCount, it might work.
1196 return info
.cpuCount
;
1199 // /proc/cpuinfo doesn't have a cross-architecture way of counting physical
1200 // cores. On x86, one could look at the number of unique combinations of
1201 // `physical id` and `core id` or `cpu cores`, but those are not present on
1202 // e.g. aarch64. (and that might not even be enough for NUMA nodes, but
1203 // realistically, there probably aren't a lot of people running this code
1204 // on such machines)
1205 // As a shortcut on x86, you'd think you could just multiply the last
1206 // physical id + 1 with the last core id + 1, but at least core ids are not
1207 // even necessarily adjacent. (notably, on 13th or 14th generation Intel
1208 // CPUs, they go in increments of 4 for performance cores, and then 1 after
1209 // hitting the first efficiency core)
1210 // /sys/devices/system/cpu/cpu*/topology/core_cpus does show which logical
1211 // cores are associated together, such that running the command:
1212 // sort -u /sys/devices/system/cpu/cpu*/topology/core_cpus | wc -l
1213 // gives a count of physical cores.
1214 // There are cpuCount /sys/devices/system/cpu/cpu* directories, and they
1215 // are monotonically increasing.
1216 // We're going to kind of do that, but reading the actual bitmasks contained
1218 constexpr int mask_bits
= sizeof(uint32_t) * 8;
1220 Vector
<uint32_t> cpumasks
;
1221 physicalCPUs
= [&] {
1223 if (!cpumasks
.appendN(0, (max_cpu_bits
+ mask_bits
- 1) / mask_bits
)) {
1226 for (int32_t cpu
= 0; cpu
< info
.cpuCount
; ++cpu
) {
1227 nsPrintfCString
core_cpus(
1228 "/sys/devices/system/cpu/cpu%d/topology/core_cpus", cpu
);
1229 std::ifstream
input(core_cpus
.Data());
1230 // Kernel versions before 5.3 didn't have core_cpus, they had
1231 // thread_siblings instead, with the same content. As of writing, kernel
1232 // version 6.9 still has both, but thread_siblings has been deprecated
1233 // since the introduction of core_cpus.
1235 core_cpus
.Truncate(core_cpus
.Length() - sizeof("core_cpus") + 1);
1236 core_cpus
.AppendLiteral("thread_siblings");
1237 input
.open(core_cpus
.Data());
1240 if (!getline(input
, line
)) {
1243 Tokenizer
p(line
.c_str());
1244 bool unknown_core
= false;
1245 // The format of the file is `bitmask0,bitmask1,..,bitmaskn`
1246 // where each bitmask is 32-bits wide, and there are as many as
1247 // necessary to print max_cpu_bits bits.
1248 for (auto& mask
: cpumasks
) {
1250 if (NS_WARN_IF(!p
.ReadHexadecimal(&m
, /* aPrefixed = */ false))) {
1253 if (!p
.CheckEOF() && !p
.CheckChar(',')) {
1256 // We're keeping track of all the CPU bits we've seen so far.
1257 // If we're now seeing one that has never been set, it means
1258 // we're seeing a new physical core (as opposed to a logical
1259 // core). We don't want to end the loop now, though, because
1260 // we also want to track all the bits we're seeing, in case
1261 // subsequent masks have new bits as well.
1262 if ((mask
& m
) != m
) {
1263 unknown_core
= true;
1275 info
.cpuCount
= PR_GetNumberOfProcessors();
1277 if (Maybe
<hal::HeterogeneousCpuInfo
> hetCpuInfo
=
1278 hal::GetHeterogeneousCpuInfo()) {
1279 info
.cpuPCount
= int32_t(hetCpuInfo
->mBigCpus
.Count());
1280 info
.cpuMCount
= int32_t(hetCpuInfo
->mMediumCpus
.Count());
1281 info
.cpuECount
= int32_t(hetCpuInfo
->mLittleCpus
.Count());
1283 info
.cpuPCount
= physicalCPUs
;
1288 if (cpuSpeed
>= 0) {
1289 info
.cpuSpeed
= cpuSpeed
;
1293 if (!cpuVendor
.IsEmpty()) {
1294 info
.cpuVendor
= cpuVendor
;
1296 if (!cpuName
.IsEmpty()) {
1297 info
.cpuName
= cpuName
;
1299 if (cpuFamily
>= 0) {
1300 info
.cpuFamily
= cpuFamily
;
1302 if (cpuModel
>= 0) {
1303 info
.cpuModel
= cpuModel
;
1305 if (cpuStepping
>= 0) {
1306 info
.cpuStepping
= cpuStepping
;
1309 if (logicalCPUs
>= 0) {
1310 info
.cpuCount
= logicalCPUs
;
1312 if (physicalCPUs
>= 0) {
1313 info
.cpuCores
= physicalCPUs
;
1316 if (cacheSizeL2
>= 0) {
1317 info
.l2cacheKB
= cacheSizeL2
;
1319 if (cacheSizeL3
>= 0) {
1320 info
.l3cacheKB
= cacheSizeL3
;
1326 #if defined(__MINGW32__)
1328 BOOL WINAPI
IsUserCetAvailableInEnvironment(_In_ DWORD UserCetEnvironment
);
1330 # define USER_CET_ENVIRONMENT_WIN32_PROCESS 0x00000000
1333 nsresult
nsSystemInfo::Init() {
1334 // check that it is called from the main thread on all platforms.
1335 MOZ_ASSERT(NS_IsMainThread());
1339 static const struct {
1342 } items
[] = {{PR_SI_SYSNAME
, "name"},
1343 {PR_SI_ARCHITECTURE
, "arch"},
1344 {PR_SI_RELEASE
, "version"},
1345 {PR_SI_RELEASE_BUILD
, "build"}};
1347 for (uint32_t i
= 0; i
< (sizeof(items
) / sizeof(items
[0])); i
++) {
1348 char buf
[SYS_INFO_BUFFER_LENGTH
];
1349 if (PR_GetSystemInfo(items
[i
].cmd
, buf
, sizeof(buf
)) == PR_SUCCESS
) {
1350 rv
= SetPropertyAsACString(NS_ConvertASCIItoUTF16(items
[i
].name
),
1351 nsDependentCString(buf
));
1352 if (NS_WARN_IF(NS_FAILED(rv
))) {
1356 NS_WARNING("PR_GetSystemInfo failed");
1360 SetPropertyAsBool(u
"isPackagedApp"_ns
, false);
1362 // Additional informations not available through PR_GetSystemInfo.
1363 SetInt32Property(u
"pagesize"_ns
, PR_GetPageSize());
1364 SetInt32Property(u
"pageshift"_ns
, PR_GetPageShift());
1365 SetInt32Property(u
"memmapalign"_ns
, PR_GetMemMapAlignment());
1366 SetUint64Property(u
"memsize"_ns
, PR_GetPhysicalMemorySize());
1367 SetUint32Property(u
"umask"_ns
, nsSystemInfo::gUserUmask
);
1369 #ifdef HAVE_64BIT_BUILD
1370 SetUint32Property(u
"archbits"_ns
, 64);
1372 SetUint32Property(u
"archbits"_ns
, 32);
1375 uint64_t virtualMem
= 0;
1379 MEMORYSTATUSEX memStat
;
1380 memStat
.dwLength
= sizeof(memStat
);
1381 if (GlobalMemoryStatusEx(&memStat
)) {
1382 virtualMem
= memStat
.ullTotalVirtual
;
1385 if (virtualMem
) SetUint64Property(u
"virtualmemsize"_ns
, virtualMem
);
1387 for (uint32_t i
= 0; i
< std::size(cpuPropItems
); i
++) {
1388 rv
= SetPropertyAsBool(NS_ConvertASCIItoUTF16(cpuPropItems
[i
].name
),
1389 cpuPropItems
[i
].propfun());
1390 if (NS_WARN_IF(NS_FAILED(rv
))) {
1402 rv
= SetPropertyAsBool(u
"isMinGW"_ns
, !!isMinGW
);
1403 if (NS_WARN_IF(NS_FAILED(rv
))) {
1407 boolean hasPackageIdentity
= widget::WinUtils::HasPackageIdentity();
1409 rv
= SetPropertyAsBool(u
"hasWinPackageId"_ns
, hasPackageIdentity
);
1410 if (NS_WARN_IF(NS_FAILED(rv
))) {
1414 rv
= SetPropertyAsAString(u
"winPackageFamilyName"_ns
,
1415 widget::WinUtils::GetPackageFamilyName());
1416 if (NS_WARN_IF(NS_FAILED(rv
))) {
1420 rv
= SetPropertyAsBool(u
"isPackagedApp"_ns
, hasPackageIdentity
);
1421 if (NS_WARN_IF(NS_FAILED(rv
))) {
1425 # ifndef __MINGW32__
1426 nsAutoString avInfo
, antiSpyInfo
, firewallInfo
;
1428 GetWindowsSecurityCenterInfo(avInfo
, antiSpyInfo
, firewallInfo
))) {
1429 if (!avInfo
.IsEmpty()) {
1430 rv
= SetPropertyAsAString(u
"registeredAntiVirus"_ns
, avInfo
);
1431 if (NS_WARN_IF(NS_FAILED(rv
))) {
1436 if (!antiSpyInfo
.IsEmpty()) {
1437 rv
= SetPropertyAsAString(u
"registeredAntiSpyware"_ns
, antiSpyInfo
);
1438 if (NS_WARN_IF(NS_FAILED(rv
))) {
1443 if (!firewallInfo
.IsEmpty()) {
1444 rv
= SetPropertyAsAString(u
"registeredFirewall"_ns
, firewallInfo
);
1445 if (NS_WARN_IF(NS_FAILED(rv
))) {
1450 # endif // __MINGW32__
1452 mozilla::DynamicallyLinkedFunctionPtr
<
1453 decltype(&IsUserCetAvailableInEnvironment
)>
1454 isUserCetAvailable(L
"api-ms-win-core-sysinfo-l1-2-6.dll",
1455 "IsUserCetAvailableInEnvironment");
1456 bool hasUserCET
= isUserCetAvailable
&&
1457 isUserCetAvailable(USER_CET_ENVIRONMENT_WIN32_PROCESS
);
1458 rv
= SetPropertyAsBool(u
"hasUserCET"_ns
, hasUserCET
);
1459 if (NS_WARN_IF(NS_FAILED(rv
))) {
1465 #if defined(XP_WIN) || defined(ANDROID)
1466 // TODO(krosylight): Enable this on other platforms too when implemented
1467 if (XRE_IsParentProcess()) {
1468 auto kinds
= static_cast<LookAndFeel::PointingDeviceKinds
>(
1469 LookAndFeel::GetInt(LookAndFeel::IntID::PointingDeviceKinds
, 0));
1470 MOZ_TRY(SetPropertyAsBool(
1471 u
"hasMouse"_ns
, !!(kinds
& LookAndFeel::PointingDeviceKinds::Mouse
)));
1472 MOZ_TRY(SetPropertyAsBool(
1473 u
"hasTouch"_ns
, !!(kinds
& LookAndFeel::PointingDeviceKinds::Touch
)));
1474 MOZ_TRY(SetPropertyAsBool(
1475 u
"hasPen"_ns
, !!(kinds
& LookAndFeel::PointingDeviceKinds::Pen
)));
1479 #if defined(XP_MACOSX)
1480 nsAutoCString modelId
;
1481 if (NS_SUCCEEDED(GetAppleModelId(modelId
))) {
1482 rv
= SetPropertyAsACString(u
"appleModelId"_ns
, modelId
);
1483 NS_ENSURE_SUCCESS(rv
, rv
);
1486 if (NS_SUCCEEDED(ProcessIsRosettaTranslated(isRosetta
))) {
1487 rv
= SetPropertyAsBool(u
"rosettaStatus"_ns
, isRosetta
);
1488 NS_ENSURE_SUCCESS(rv
, rv
);
1493 nsAutoCString themeInfo
;
1494 LookAndFeel::GetThemeInfo(themeInfo
);
1495 MOZ_TRY(SetPropertyAsACString(u
"osThemeInfo"_ns
, themeInfo
));
1498 #if defined(MOZ_WIDGET_GTK)
1499 // This must be done here because NSPR can only separate OS's when compiled,
1500 // not libraries. 64 bytes is going to be well enough for "GTK " followed by 3
1501 // integers separated with dots.
1503 ssize_t gtkver_len
= 0;
1505 if (gtkver_len
<= 0) {
1506 gtkver_len
= SprintfLiteral(gtkver
, "GTK %u.%u.%u", gtk_major_version
,
1507 gtk_minor_version
, gtk_micro_version
);
1510 nsAutoCString secondaryLibrary
;
1511 if (gtkver_len
> 0 && gtkver_len
< int(sizeof(gtkver
))) {
1512 secondaryLibrary
.Append(nsDependentCSubstring(gtkver
, gtkver_len
));
1516 // With TSan, avoid loading libpulse here because we cannot unload it
1517 // afterwards due to restrictions from TSan about unloading libraries
1518 // matched by the suppression list.
1519 void* libpulse
= dlopen("libpulse.so.0", RTLD_LAZY
);
1520 const char* libpulseVersion
= "not-available";
1522 auto pa_get_library_version
= reinterpret_cast<const char* (*)()>(
1523 dlsym(libpulse
, "pa_get_library_version"));
1525 if (pa_get_library_version
) {
1526 libpulseVersion
= pa_get_library_version();
1530 secondaryLibrary
.AppendPrintf(",libpulse %s", libpulseVersion
);
1537 rv
= SetPropertyAsACString(u
"secondaryLibrary"_ns
, secondaryLibrary
);
1538 if (NS_WARN_IF(NS_FAILED(rv
))) {
1541 rv
= SetPropertyAsBool(u
"isPackagedApp"_ns
,
1542 widget::IsRunningUnderFlatpakOrSnap() ||
1543 widget::IsPackagedAppFileExists());
1544 if (NS_WARN_IF(NS_FAILED(rv
))) {
1549 #ifdef MOZ_WIDGET_ANDROID
1550 AndroidSystemInfo info
;
1551 GetAndroidSystemInfo(&info
);
1552 SetupAndroidInfo(info
);
1555 #if defined(XP_LINUX) && !defined(ANDROID)
1556 nsCString dist
, desc
, release
, codename
;
1557 if (widget::lsb::GetLSBRelease(dist
, desc
, release
, codename
)) {
1558 SetPropertyAsACString(u
"distro"_ns
, dist
);
1559 SetPropertyAsACString(u
"distroVersion"_ns
, release
);
1563 #if defined(XP_LINUX) && defined(MOZ_SANDBOX)
1564 SandboxInfo sandInfo
= SandboxInfo::Get();
1566 SetPropertyAsBool(u
"hasSeccompBPF"_ns
,
1567 sandInfo
.Test(SandboxInfo::kHasSeccompBPF
));
1568 SetPropertyAsBool(u
"hasSeccompTSync"_ns
,
1569 sandInfo
.Test(SandboxInfo::kHasSeccompTSync
));
1570 SetPropertyAsBool(u
"hasUserNamespaces"_ns
,
1571 sandInfo
.Test(SandboxInfo::kHasUserNamespaces
));
1572 SetPropertyAsBool(u
"hasPrivilegedUserNamespaces"_ns
,
1573 sandInfo
.Test(SandboxInfo::kHasPrivilegedUserNamespaces
));
1575 if (sandInfo
.Test(SandboxInfo::kEnabledForContent
)) {
1576 SetPropertyAsBool(u
"canSandboxContent"_ns
, sandInfo
.CanSandboxContent());
1579 if (sandInfo
.Test(SandboxInfo::kEnabledForMedia
)) {
1580 SetPropertyAsBool(u
"canSandboxMedia"_ns
, sandInfo
.CanSandboxMedia());
1582 #endif // XP_LINUX && MOZ_SANDBOX
1587 #ifdef MOZ_WIDGET_ANDROID
1588 // Prerelease versions of Android use a letter instead of version numbers.
1589 // Unfortunately this breaks websites due to the user agent.
1590 // Chrome works around this by hardcoding an Android version when a
1591 // numeric version can't be obtained. We're doing the same.
1592 // This version will need to be updated whenever there is a new official
1593 // Android release. Search for "kDefaultAndroidMajorVersion" in:
1594 // https://source.chromium.org/chromium/chromium/src/+/master:base/system/sys_info_android.cc
1595 # define DEFAULT_ANDROID_VERSION u"10.0.99"
1598 void nsSystemInfo::GetAndroidSystemInfo(AndroidSystemInfo
* aInfo
) {
1599 if (!jni::IsAvailable()) {
1600 // called from xpcshell etc.
1601 aInfo
->sdk_version() = 0;
1605 jni::String::LocalRef model
= java::sdk::Build::MODEL();
1606 aInfo
->device() = model
->ToString();
1608 jni::String::LocalRef manufacturer
=
1609 mozilla::java::sdk::Build::MANUFACTURER();
1610 aInfo
->manufacturer() = manufacturer
->ToString();
1612 jni::String::LocalRef hardware
= java::sdk::Build::HARDWARE();
1613 aInfo
->hardware() = hardware
->ToString();
1615 jni::String::LocalRef release
= java::sdk::Build::VERSION::RELEASE();
1616 nsString
str(release
->ToString());
1620 int num_read
= sscanf(NS_ConvertUTF16toUTF8(str
).get(), "%d.%d.%d",
1621 &major_version
, &minor_version
, &bugfix_version
);
1622 if (num_read
== 0) {
1623 aInfo
->release_version() = nsLiteralString(DEFAULT_ANDROID_VERSION
);
1625 aInfo
->release_version() = str
;
1628 aInfo
->sdk_version() = jni::GetAPIVersion();
1629 aInfo
->isTablet() = java::GeckoAppShell::IsTablet();
1632 void nsSystemInfo::SetupAndroidInfo(const AndroidSystemInfo
& aInfo
) {
1633 if (!aInfo
.device().IsEmpty()) {
1634 SetPropertyAsAString(u
"device"_ns
, aInfo
.device());
1636 if (!aInfo
.manufacturer().IsEmpty()) {
1637 SetPropertyAsAString(u
"manufacturer"_ns
, aInfo
.manufacturer());
1639 if (!aInfo
.release_version().IsEmpty()) {
1640 SetPropertyAsAString(u
"release_version"_ns
, aInfo
.release_version());
1642 SetPropertyAsBool(u
"tablet"_ns
, aInfo
.isTablet());
1643 // NSPR "version" is the kernel version. For Android we want the Android
1644 // version. Rename SDK version to version and put the kernel version into
1647 nsresult rv
= GetPropertyAsAString(u
"version"_ns
, str
);
1648 if (NS_SUCCEEDED(rv
)) {
1649 SetPropertyAsAString(u
"kernel_version"_ns
, str
);
1651 // When JNI is not available (eg. in xpcshell tests), sdk_version is 0.
1652 if (aInfo
.sdk_version() != 0) {
1653 if (!aInfo
.hardware().IsEmpty()) {
1654 SetPropertyAsAString(u
"hardware"_ns
, aInfo
.hardware());
1656 SetPropertyAsInt32(u
"version"_ns
, aInfo
.sdk_version());
1659 #endif // MOZ_WIDGET_ANDROID
1661 void nsSystemInfo::SetInt32Property(const nsAString
& aPropertyName
,
1662 const int32_t aValue
) {
1663 NS_WARNING_ASSERTION(aValue
> 0, "Unable to read system value");
1668 SetPropertyAsInt32(aPropertyName
, aValue
);
1669 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv
), "Unable to set property");
1673 void nsSystemInfo::SetUint32Property(const nsAString
& aPropertyName
,
1674 const uint32_t aValue
) {
1675 // Only one property is currently set via this function.
1676 // It may legitimately be zero.
1680 SetPropertyAsUint32(aPropertyName
, aValue
);
1681 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv
), "Unable to set property");
1684 void nsSystemInfo::SetUint64Property(const nsAString
& aPropertyName
,
1685 const uint64_t aValue
) {
1686 NS_WARNING_ASSERTION(aValue
> 0, "Unable to read system value");
1691 SetPropertyAsUint64(aPropertyName
, aValue
);
1692 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv
), "Unable to set property");
1698 static bool GetJSObjForDiskInfo(JSContext
* aCx
, JS::Handle
<JSObject
*> aParent
,
1699 const FolderDiskInfo
& info
,
1700 const char* propName
) {
1701 JS::Rooted
<JSObject
*> jsInfo(aCx
, JS_NewPlainObject(aCx
));
1706 JSString
* strModel
=
1707 JS_NewStringCopyN(aCx
, info
.model
.get(), info
.model
.Length());
1711 JS::Rooted
<JS::Value
> valModel(aCx
, JS::StringValue(strModel
));
1712 if (!JS_SetProperty(aCx
, jsInfo
, "model", valModel
)) {
1716 JSString
* strRevision
=
1717 JS_NewStringCopyN(aCx
, info
.revision
.get(), info
.revision
.Length());
1721 JS::Rooted
<JS::Value
> valRevision(aCx
, JS::StringValue(strRevision
));
1722 if (!JS_SetProperty(aCx
, jsInfo
, "revision", valRevision
)) {
1726 JSString
* strSSD
= JS_NewStringCopyZ(aCx
, info
.isSSD
? "SSD" : "HDD");
1730 JS::Rooted
<JS::Value
> valSSD(aCx
, JS::StringValue(strSSD
));
1731 if (!JS_SetProperty(aCx
, jsInfo
, "type", valSSD
)) {
1735 JS::Rooted
<JS::Value
> val(aCx
, JS::ObjectValue(*jsInfo
));
1736 return JS_SetProperty(aCx
, aParent
, propName
, val
);
1739 JSObject
* GetJSObjForOSInfo(JSContext
* aCx
, const OSInfo
& info
) {
1740 JS::Rooted
<JSObject
*> jsInfo(aCx
, JS_NewPlainObject(aCx
));
1742 JS::Rooted
<JS::Value
> valInstallYear(aCx
, JS::Int32Value(info
.installYear
));
1743 JS_SetProperty(aCx
, jsInfo
, "installYear", valInstallYear
);
1745 JS::Rooted
<JS::Value
> valHasSuperfetch(aCx
,
1746 JS::BooleanValue(info
.hasSuperfetch
));
1747 JS_SetProperty(aCx
, jsInfo
, "hasSuperfetch", valHasSuperfetch
);
1749 JS::Rooted
<JS::Value
> valHasPrefetch(aCx
, JS::BooleanValue(info
.hasPrefetch
));
1750 JS_SetProperty(aCx
, jsInfo
, "hasPrefetch", valHasPrefetch
);
1757 JSObject
* GetJSObjForProcessInfo(JSContext
* aCx
, const ProcessInfo
& info
) {
1758 JS::Rooted
<JSObject
*> jsInfo(aCx
, JS_NewPlainObject(aCx
));
1761 JS::Rooted
<JS::Value
> valisWow64(aCx
, JS::BooleanValue(info
.isWow64
));
1762 JS_SetProperty(aCx
, jsInfo
, "isWow64", valisWow64
);
1764 JS::Rooted
<JS::Value
> valisWowARM64(aCx
, JS::BooleanValue(info
.isWowARM64
));
1765 JS_SetProperty(aCx
, jsInfo
, "isWowARM64", valisWowARM64
);
1767 JS::Rooted
<JS::Value
> valisWindowsSMode(
1768 aCx
, JS::BooleanValue(info
.isWindowsSMode
));
1769 JS_SetProperty(aCx
, jsInfo
, "isWindowsSMode", valisWindowsSMode
);
1772 JS::Rooted
<JS::Value
> valCountInfo(aCx
, JS::Int32Value(info
.cpuCount
));
1773 JS_SetProperty(aCx
, jsInfo
, "count", valCountInfo
);
1775 JS::Rooted
<JS::Value
> valCoreInfo(
1776 aCx
, info
.cpuCores
? JS::Int32Value(info
.cpuCores
) : JS::NullValue());
1777 JS_SetProperty(aCx
, jsInfo
, "cores", valCoreInfo
);
1779 JS::Rooted
<JS::Value
> valPCountInfo(
1780 aCx
, info
.cpuCores
? JS::Int32Value(info
.cpuPCount
) : JS::NullValue());
1781 JS_SetProperty(aCx
, jsInfo
, "pcount", valPCountInfo
);
1783 JS::Rooted
<JS::Value
> valMCountInfo(
1784 aCx
, info
.cpuCores
? JS::Int32Value(info
.cpuMCount
) : JS::NullValue());
1785 JS_SetProperty(aCx
, jsInfo
, "mcount", valMCountInfo
);
1787 JS::Rooted
<JS::Value
> valECountInfo(
1788 aCx
, info
.cpuCores
? JS::Int32Value(info
.cpuECount
) : JS::NullValue());
1789 JS_SetProperty(aCx
, jsInfo
, "ecount", valECountInfo
);
1791 JSString
* strVendor
=
1792 JS_NewStringCopyN(aCx
, info
.cpuVendor
.get(), info
.cpuVendor
.Length());
1793 JS::Rooted
<JS::Value
> valVendor(aCx
, JS::StringValue(strVendor
));
1794 JS_SetProperty(aCx
, jsInfo
, "vendor", valVendor
);
1797 JS_NewStringCopyN(aCx
, info
.cpuName
.get(), info
.cpuName
.Length());
1798 JS::Rooted
<JS::Value
> valName(aCx
, JS::StringValue(strName
));
1799 JS_SetProperty(aCx
, jsInfo
, "name", valName
);
1801 JS::Rooted
<JS::Value
> valFamilyInfo(aCx
, JS::Int32Value(info
.cpuFamily
));
1802 JS_SetProperty(aCx
, jsInfo
, "family", valFamilyInfo
);
1804 JS::Rooted
<JS::Value
> valModelInfo(aCx
, JS::Int32Value(info
.cpuModel
));
1805 JS_SetProperty(aCx
, jsInfo
, "model", valModelInfo
);
1807 JS::Rooted
<JS::Value
> valSteppingInfo(aCx
, JS::Int32Value(info
.cpuStepping
));
1808 JS_SetProperty(aCx
, jsInfo
, "stepping", valSteppingInfo
);
1810 JS::Rooted
<JS::Value
> valL2CacheInfo(aCx
, JS::Int32Value(info
.l2cacheKB
));
1811 JS_SetProperty(aCx
, jsInfo
, "l2cacheKB", valL2CacheInfo
);
1813 JS::Rooted
<JS::Value
> valL3CacheInfo(aCx
, JS::Int32Value(info
.l3cacheKB
));
1814 JS_SetProperty(aCx
, jsInfo
, "l3cacheKB", valL3CacheInfo
);
1816 JS::Rooted
<JS::Value
> valSpeedInfo(aCx
, JS::Int32Value(info
.cpuSpeed
));
1817 JS_SetProperty(aCx
, jsInfo
, "speedMHz", valSpeedInfo
);
1822 RefPtr
<nsISerialEventTarget
> nsSystemInfo::GetBackgroundTarget() {
1823 if (!mBackgroundET
) {
1824 MOZ_ALWAYS_SUCCEEDS(NS_CreateBackgroundTaskQueue(
1825 "SystemInfoThread", getter_AddRefs(mBackgroundET
)));
1827 return mBackgroundET
;
1831 nsSystemInfo::GetOsInfo(JSContext
* aCx
, Promise
** aResult
) {
1832 NS_ENSURE_ARG_POINTER(aResult
);
1834 if (!XRE_IsParentProcess()) {
1835 return NS_ERROR_FAILURE
;
1838 nsIGlobalObject
* global
= xpc::CurrentNativeGlobal(aCx
);
1839 if (NS_WARN_IF(!global
)) {
1840 return NS_ERROR_FAILURE
;
1844 RefPtr
<Promise
> promise
= Promise::Create(global
, erv
);
1845 if (NS_WARN_IF(erv
.Failed())) {
1846 return erv
.StealNSResult();
1849 if (!mOSInfoPromise
) {
1850 RefPtr
<nsISerialEventTarget
> backgroundET
= GetBackgroundTarget();
1852 mOSInfoPromise
= InvokeAsync(backgroundET
, __func__
, []() {
1854 nsresult rv
= CollectOSInfo(info
);
1855 if (NS_SUCCEEDED(rv
)) {
1856 return OSInfoPromise::CreateAndResolve(info
, __func__
);
1858 return OSInfoPromise::CreateAndReject(rv
, __func__
);
1862 // Chain the new promise to the extant mozpromise
1863 RefPtr
<Promise
> capturedPromise
= promise
;
1864 mOSInfoPromise
->Then(
1865 GetMainThreadSerialEventTarget(), __func__
,
1866 [capturedPromise
](const OSInfo
& info
) {
1868 if (NS_WARN_IF(!jsapi
.Init(capturedPromise
->GetGlobalObject()))) {
1869 capturedPromise
->MaybeReject(NS_ERROR_UNEXPECTED
);
1872 JSContext
* cx
= jsapi
.cx();
1873 JS::Rooted
<JS::Value
> val(
1874 cx
, JS::ObjectValue(*GetJSObjForOSInfo(cx
, info
)));
1875 capturedPromise
->MaybeResolve(val
);
1877 [capturedPromise
](const nsresult rv
) {
1878 // Resolve with null when installYear is not available from the system
1879 capturedPromise
->MaybeResolve(JS::NullHandleValue
);
1882 promise
.forget(aResult
);
1888 nsSystemInfo::GetDiskInfo(JSContext
* aCx
, Promise
** aResult
) {
1889 NS_ENSURE_ARG_POINTER(aResult
);
1891 if (!XRE_IsParentProcess()) {
1892 return NS_ERROR_FAILURE
;
1895 nsIGlobalObject
* global
= xpc::CurrentNativeGlobal(aCx
);
1896 if (NS_WARN_IF(!global
)) {
1897 return NS_ERROR_FAILURE
;
1900 RefPtr
<Promise
> promise
= Promise::Create(global
, erv
);
1901 if (NS_WARN_IF(erv
.Failed())) {
1902 return erv
.StealNSResult();
1905 if (!mDiskInfoPromise
) {
1906 RefPtr
<nsISerialEventTarget
> backgroundET
= GetBackgroundTarget();
1907 nsCOMPtr
<nsIFile
> greDir
;
1908 nsCOMPtr
<nsIFile
> winDir
;
1909 nsCOMPtr
<nsIFile
> profDir
;
1910 nsresult rv
= NS_GetSpecialDirectory(NS_GRE_DIR
, getter_AddRefs(greDir
));
1911 if (NS_FAILED(rv
)) {
1914 rv
= NS_GetSpecialDirectory(NS_WIN_WINDOWS_DIR
, getter_AddRefs(winDir
));
1915 if (NS_FAILED(rv
)) {
1918 rv
= NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR
,
1919 getter_AddRefs(profDir
));
1920 if (NS_FAILED(rv
)) {
1925 InvokeAsync(backgroundET
, __func__
, [greDir
, winDir
, profDir
]() {
1927 nsresult rv
= CollectDiskInfo(greDir
, winDir
, profDir
, info
);
1928 if (NS_SUCCEEDED(rv
)) {
1929 return DiskInfoPromise::CreateAndResolve(info
, __func__
);
1931 return DiskInfoPromise::CreateAndReject(rv
, __func__
);
1935 // Chain the new promise to the extant mozpromise.
1936 RefPtr
<Promise
> capturedPromise
= promise
;
1937 mDiskInfoPromise
->Then(
1938 GetMainThreadSerialEventTarget(), __func__
,
1939 [capturedPromise
](const DiskInfo
& info
) {
1941 if (NS_WARN_IF(!jsapi
.Init(capturedPromise
->GetGlobalObject()))) {
1942 capturedPromise
->MaybeReject(NS_ERROR_UNEXPECTED
);
1945 JSContext
* cx
= jsapi
.cx();
1946 JS::Rooted
<JSObject
*> jsInfo(cx
, JS_NewPlainObject(cx
));
1947 // Store data in the rv:
1948 bool succeededSettingAllObjects
=
1949 jsInfo
&& GetJSObjForDiskInfo(cx
, jsInfo
, info
.binary
, "binary") &&
1950 GetJSObjForDiskInfo(cx
, jsInfo
, info
.profile
, "profile") &&
1951 GetJSObjForDiskInfo(cx
, jsInfo
, info
.system
, "system");
1952 // The above can fail due to OOM
1953 if (!succeededSettingAllObjects
) {
1954 JS_ClearPendingException(cx
);
1955 capturedPromise
->MaybeReject(NS_ERROR_FAILURE
);
1959 JS::Rooted
<JS::Value
> val(cx
, JS::ObjectValue(*jsInfo
));
1960 capturedPromise
->MaybeResolve(val
);
1962 [capturedPromise
](const nsresult rv
) {
1963 capturedPromise
->MaybeReject(rv
);
1966 promise
.forget(aResult
);
1971 NS_IMPL_ISUPPORTS_INHERITED(nsSystemInfo
, nsHashPropertyBag
, nsISystemInfo
)
1974 nsSystemInfo::GetCountryCode(JSContext
* aCx
, Promise
** aResult
) {
1975 NS_ENSURE_ARG_POINTER(aResult
);
1978 if (!XRE_IsParentProcess()) {
1979 return NS_ERROR_FAILURE
;
1981 #if defined(XP_MACOSX) || defined(XP_WIN)
1982 nsIGlobalObject
* global
= xpc::CurrentNativeGlobal(aCx
);
1983 if (NS_WARN_IF(!global
)) {
1984 return NS_ERROR_FAILURE
;
1988 RefPtr
<Promise
> promise
= Promise::Create(global
, erv
);
1989 if (NS_WARN_IF(erv
.Failed())) {
1990 return erv
.StealNSResult();
1993 if (!mCountryCodePromise
) {
1994 RefPtr
<nsISerialEventTarget
> backgroundET
= GetBackgroundTarget();
1996 mCountryCodePromise
= InvokeAsync(backgroundET
, __func__
, []() {
1997 nsAutoString countryCode
;
1999 nsresult rv
= GetSelectedCityInfo(countryCode
);
2002 nsresult rv
= CollectCountryCode(countryCode
);
2005 if (NS_SUCCEEDED(rv
)) {
2006 return CountryCodePromise::CreateAndResolve(countryCode
, __func__
);
2008 return CountryCodePromise::CreateAndReject(rv
, __func__
);
2012 RefPtr
<Promise
> capturedPromise
= promise
;
2013 mCountryCodePromise
->Then(
2014 GetMainThreadSerialEventTarget(), __func__
,
2015 [capturedPromise
](const nsString
& countryCode
) {
2017 if (NS_WARN_IF(!jsapi
.Init(capturedPromise
->GetGlobalObject()))) {
2018 capturedPromise
->MaybeReject(NS_ERROR_UNEXPECTED
);
2021 JSContext
* cx
= jsapi
.cx();
2022 JS::Rooted
<JSString
*> jsCountryCode(
2023 cx
, JS_NewUCStringCopyZ(cx
, countryCode
.get()));
2025 JS::Rooted
<JS::Value
> val(cx
, JS::StringValue(jsCountryCode
));
2026 capturedPromise
->MaybeResolve(val
);
2028 [capturedPromise
](const nsresult rv
) {
2029 // Resolve with null when countryCode is not available from the system
2030 capturedPromise
->MaybeResolve(JS::NullHandleValue
);
2033 promise
.forget(aResult
);
2039 nsSystemInfo::GetProcessInfo(JSContext
* aCx
, Promise
** aResult
) {
2040 NS_ENSURE_ARG_POINTER(aResult
);
2043 if (!XRE_IsParentProcess()) {
2044 return NS_ERROR_FAILURE
;
2047 nsIGlobalObject
* global
= xpc::CurrentNativeGlobal(aCx
);
2048 if (NS_WARN_IF(!global
)) {
2049 return NS_ERROR_FAILURE
;
2053 RefPtr
<Promise
> promise
= Promise::Create(global
, erv
);
2054 if (NS_WARN_IF(erv
.Failed())) {
2055 return erv
.StealNSResult();
2058 if (!mProcessInfoPromise
) {
2059 RefPtr
<nsISerialEventTarget
> backgroundET
= GetBackgroundTarget();
2061 mProcessInfoPromise
= InvokeAsync(backgroundET
, __func__
, []() {
2063 nsresult rv
= CollectProcessInfo(info
);
2064 if (NS_SUCCEEDED(rv
)) {
2065 return ProcessInfoPromise::CreateAndResolve(info
, __func__
);
2067 return ProcessInfoPromise::CreateAndReject(rv
, __func__
);
2071 // Chain the new promise to the extant mozpromise
2072 RefPtr
<Promise
> capturedPromise
= promise
;
2073 mProcessInfoPromise
->Then(
2074 GetMainThreadSerialEventTarget(), __func__
,
2075 [capturedPromise
](const ProcessInfo
& info
) {
2077 if (NS_WARN_IF(!jsapi
.Init(capturedPromise
->GetGlobalObject()))) {
2078 capturedPromise
->MaybeReject(NS_ERROR_UNEXPECTED
);
2081 JSContext
* cx
= jsapi
.cx();
2082 JS::Rooted
<JS::Value
> val(
2083 cx
, JS::ObjectValue(*GetJSObjForProcessInfo(cx
, info
)));
2084 capturedPromise
->MaybeResolve(val
);
2086 [capturedPromise
](const nsresult rv
) {
2087 // Resolve with null when installYear is not available from the system
2088 capturedPromise
->MaybeResolve(JS::NullHandleValue
);
2091 promise
.forget(aResult
);