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/LazyIdleThread.h"
16 #include "mozilla/LookAndFeel.h"
17 #include "mozilla/Sprintf.h"
18 #include "mozilla/Try.h"
20 #include "js/PropertyAndElement.h" // JS_SetProperty
21 #include "mozilla/dom/Promise.h"
28 # endif // __MINGW32__
30 # include <winioctl.h>
34 # endif // __MINGW32__
35 # include "base/scoped_handle_win.h"
36 # include "mozilla/DynamicallyLinkedFunctionPtr.h"
37 # include "mozilla/WindowsVersion.h"
38 # include "nsAppDirectoryServiceDefs.h"
39 # include "nsDirectoryServiceDefs.h"
40 # include "nsDirectoryServiceUtils.h"
41 # include "nsWindowsHelpers.h"
42 # include "WinUtils.h"
43 # include "mozilla/NotNull.h"
48 # include "MacHelpers.h"
54 # include "mozilla/WidgetUtilsGtk.h"
57 #if defined(XP_LINUX) && !defined(ANDROID)
60 # include "mozilla/Tokenizer.h"
61 # include "mozilla/widget/LSBUtils.h"
62 # include "nsCharSeparatedTokenizer.h"
68 #ifdef MOZ_WIDGET_ANDROID
69 # include "AndroidBuild.h"
70 # include "mozilla/java/GeckoAppShellWrappers.h"
71 # include "mozilla/jni/Utils.h"
75 # include <sys/sysctl.h>
78 #if defined(XP_LINUX) && defined(MOZ_SANDBOX)
79 # include "mozilla/SandboxInfo.h"
82 // Slot for NS_InitXPCOM to pass information to nsSystemInfo::Init.
83 // Only set to nonzero (potentially) if XP_UNIX. On such systems, the
84 // system call to discover the appropriate value is not thread-safe,
85 // so we must call it before going multithreaded, but nsSystemInfo::Init
86 // only happens well after that point.
87 uint32_t nsSystemInfo::gUserUmask
= 0;
89 using namespace mozilla::dom
;
92 # define RuntimeClass_Windows_System_Profile_WindowsIntegrityPolicy \
93 L"Windows.System.Profile.WindowsIntegrityPolicy"
95 using namespace Microsoft::WRL
;
96 using namespace Microsoft::WRL::Wrappers
;
97 using namespace ABI::Windows::Foundation
;
98 # endif // __MINGW32__
101 #if defined(XP_LINUX) && !defined(ANDROID)
102 static void SimpleParseKeyValuePairs(
103 const std::string
& aFilename
,
104 std::map
<nsCString
, nsCString
>& aKeyValuePairs
) {
105 std::ifstream
input(aFilename
.c_str());
106 for (std::string line
; std::getline(input
, line
);) {
107 nsAutoCString key
, value
;
109 nsCCharSeparatedTokenizer
tokens(nsDependentCString(line
.c_str()), ':');
110 if (tokens
.hasMoreTokens()) {
111 key
= tokens
.nextToken();
112 if (tokens
.hasMoreTokens()) {
113 value
= tokens
.nextToken();
115 // We want the value even if there was just one token, to cover the
116 // case where we had the key, and the value was blank (seems to be
117 // a valid scenario some files.)
118 aKeyValuePairs
[key
] = value
;
125 // Lifted from media/webrtc/trunk/webrtc/base/systeminfo.cc,
126 // so keeping the _ instead of switching to camel case for now.
127 static void GetProcessorInformation(int* physical_cpus
, int* cache_size_L2
,
128 int* cache_size_L3
) {
129 MOZ_ASSERT(physical_cpus
&& cache_size_L2
&& cache_size_L3
);
132 *cache_size_L2
= 0; // This will be in kbytes
133 *cache_size_L3
= 0; // This will be in kbytes
135 // Determine buffer size, allocate and get processor information.
136 // Size can change between calls (unlikely), so a loop is done.
137 SYSTEM_LOGICAL_PROCESSOR_INFORMATION info_buffer
[32];
138 SYSTEM_LOGICAL_PROCESSOR_INFORMATION
* infos
= &info_buffer
[0];
139 DWORD return_length
= sizeof(info_buffer
);
140 while (!::GetLogicalProcessorInformation(infos
, &return_length
)) {
141 if (GetLastError() == ERROR_INSUFFICIENT_BUFFER
&&
142 infos
== &info_buffer
[0]) {
143 infos
= new SYSTEM_LOGICAL_PROCESSOR_INFORMATION
144 [return_length
/ sizeof(SYSTEM_LOGICAL_PROCESSOR_INFORMATION
)];
151 i
< return_length
/ sizeof(SYSTEM_LOGICAL_PROCESSOR_INFORMATION
); ++i
) {
152 if (infos
[i
].Relationship
== RelationProcessorCore
) {
154 } else if (infos
[i
].Relationship
== RelationCache
) {
155 // Only care about L2 and L3 cache
156 switch (infos
[i
].Cache
.Level
) {
158 *cache_size_L2
= static_cast<int>(infos
[i
].Cache
.Size
/ 1024);
161 *cache_size_L3
= static_cast<int>(infos
[i
].Cache
.Size
/ 1024);
168 if (infos
!= &info_buffer
[0]) {
177 static nsresult
GetFolderDiskInfo(nsIFile
* file
, FolderDiskInfo
& info
) {
178 info
.model
.Truncate();
179 info
.revision
.Truncate();
182 nsAutoString filePath
;
183 nsresult rv
= file
->GetPath(filePath
);
184 NS_ENSURE_SUCCESS(rv
, rv
);
185 wchar_t volumeMountPoint
[MAX_PATH
] = {L
'\\', L
'\\', L
'.', L
'\\'};
186 const size_t PREFIX_LEN
= 4;
187 if (!::GetVolumePathNameW(
188 filePath
.get(), volumeMountPoint
+ PREFIX_LEN
,
189 mozilla::ArrayLength(volumeMountPoint
) - PREFIX_LEN
)) {
190 return NS_ERROR_UNEXPECTED
;
192 size_t volumeMountPointLen
= wcslen(volumeMountPoint
);
193 // Since we would like to open a drive and not a directory, we need to
194 // remove any trailing backslash. A drive handle is valid for
195 // DeviceIoControl calls, a directory handle is not.
196 if (volumeMountPoint
[volumeMountPointLen
- 1] == L
'\\') {
197 volumeMountPoint
[volumeMountPointLen
- 1] = L
'\0';
199 ScopedHandle
handle(::CreateFileW(volumeMountPoint
, 0,
200 FILE_SHARE_READ
| FILE_SHARE_WRITE
, nullptr,
201 OPEN_EXISTING
, 0, nullptr));
202 if (!handle
.IsValid()) {
203 return NS_ERROR_UNEXPECTED
;
205 STORAGE_PROPERTY_QUERY queryParameters
= {StorageDeviceProperty
,
206 PropertyStandardQuery
};
207 STORAGE_DEVICE_DESCRIPTOR outputHeader
= {sizeof(STORAGE_DEVICE_DESCRIPTOR
)};
209 if (!::DeviceIoControl(handle
, IOCTL_STORAGE_QUERY_PROPERTY
, &queryParameters
,
210 sizeof(queryParameters
), &outputHeader
,
211 sizeof(outputHeader
), &bytesRead
, nullptr)) {
212 return NS_ERROR_FAILURE
;
214 PSTORAGE_DEVICE_DESCRIPTOR deviceOutput
=
215 (PSTORAGE_DEVICE_DESCRIPTOR
)malloc(outputHeader
.Size
);
216 if (!::DeviceIoControl(handle
, IOCTL_STORAGE_QUERY_PROPERTY
, &queryParameters
,
217 sizeof(queryParameters
), deviceOutput
,
218 outputHeader
.Size
, &bytesRead
, nullptr)) {
220 return NS_ERROR_FAILURE
;
223 queryParameters
.PropertyId
= StorageDeviceTrimProperty
;
226 DEVICE_TRIM_DESCRIPTOR trimDescriptor
= {sizeof(DEVICE_TRIM_DESCRIPTOR
)};
227 if (::DeviceIoControl(handle
, IOCTL_STORAGE_QUERY_PROPERTY
, &queryParameters
,
228 sizeof(queryParameters
), &trimDescriptor
,
229 sizeof(trimDescriptor
), &bytesRead
, nullptr)) {
230 if (trimDescriptor
.TrimEnabled
) {
237 queryParameters
.PropertyId
= StorageDeviceSeekPenaltyProperty
;
239 DEVICE_SEEK_PENALTY_DESCRIPTOR seekPenaltyDescriptor
= {
240 sizeof(DEVICE_SEEK_PENALTY_DESCRIPTOR
)};
241 if (::DeviceIoControl(handle
, IOCTL_STORAGE_QUERY_PROPERTY
,
242 &queryParameters
, sizeof(queryParameters
),
243 &seekPenaltyDescriptor
, sizeof(seekPenaltyDescriptor
),
244 &bytesRead
, nullptr)) {
245 // It is possible that the disk has TrimEnabled, but also
246 // IncursSeekPenalty; In this case, this is an HDD
247 if (seekPenaltyDescriptor
.IncursSeekPenalty
) {
253 // Some HDDs are including product ID info in the vendor field. Since PNP
254 // IDs include vendor info and product ID concatenated together, we'll do
255 // that here and interpret the result as a unique ID for the HDD model.
256 if (deviceOutput
->VendorIdOffset
) {
258 reinterpret_cast<char*>(deviceOutput
) + deviceOutput
->VendorIdOffset
;
260 if (deviceOutput
->ProductIdOffset
) {
262 reinterpret_cast<char*>(deviceOutput
) + deviceOutput
->ProductIdOffset
;
264 info
.model
.CompressWhitespace();
265 if (deviceOutput
->ProductRevisionOffset
) {
266 info
.revision
= reinterpret_cast<char*>(deviceOutput
) +
267 deviceOutput
->ProductRevisionOffset
;
268 info
.revision
.CompressWhitespace();
275 static nsresult
CollectDiskInfo(nsIFile
* greDir
, nsIFile
* winDir
,
276 nsIFile
* profDir
, DiskInfo
& info
) {
277 nsresult rv
= GetFolderDiskInfo(greDir
, info
.binary
);
281 rv
= GetFolderDiskInfo(winDir
, info
.system
);
285 return GetFolderDiskInfo(profDir
, info
.profile
);
288 static nsresult
CollectOSInfo(OSInfo
& info
) {
289 HKEY installYearHKey
;
290 LONG status
= RegOpenKeyExW(
291 HKEY_LOCAL_MACHINE
, L
"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion", 0,
292 KEY_READ
| KEY_WOW64_64KEY
, &installYearHKey
);
294 if (status
!= ERROR_SUCCESS
) {
295 return NS_ERROR_UNEXPECTED
;
298 nsAutoRegKey
installYearKey(installYearHKey
);
302 DWORD time_size
= sizeof(time_t);
304 status
= RegQueryValueExW(installYearHKey
, L
"InstallDate", nullptr, &type
,
305 (LPBYTE
)&raw_time
, &time_size
);
307 if (status
!= ERROR_SUCCESS
) {
308 return NS_ERROR_UNEXPECTED
;
311 if (type
!= REG_DWORD
) {
312 return NS_ERROR_UNEXPECTED
;
316 if (localtime_s(&time
, &raw_time
) != 0) {
317 return NS_ERROR_UNEXPECTED
;
320 info
.installYear
= 1900UL + time
.tm_year
;
322 nsAutoServiceHandle
scm(
323 OpenSCManager(nullptr, SERVICES_ACTIVE_DATABASE
, SC_MANAGER_CONNECT
));
326 return NS_ERROR_UNEXPECTED
;
329 bool superfetchServiceRunning
= false;
331 // Superfetch was introduced in Windows Vista as a service with the name
332 // SysMain. The service display name was also renamed to SysMain after Windows
334 nsAutoServiceHandle
hService(OpenService(scm
, L
"SysMain", GENERIC_READ
));
337 SERVICE_STATUS superfetchStatus
;
338 LPSERVICE_STATUS pSuperfetchStatus
= &superfetchStatus
;
340 if (!QueryServiceStatus(hService
, pSuperfetchStatus
)) {
341 return NS_ERROR_UNEXPECTED
;
344 superfetchServiceRunning
=
345 superfetchStatus
.dwCurrentState
== SERVICE_RUNNING
;
348 // If the SysMain (Superfetch) service is available, but not configured using
349 // the defaults, then it's disabled for our purposes, since it's not going to
350 // be operating as expected.
351 bool superfetchUsingDefaultParams
= true;
352 bool prefetchUsingDefaultParams
= true;
354 static const WCHAR prefetchParamsKeyName
[] =
355 L
"SYSTEM\\CurrentControlSet\\Control\\Session Manager\\Memory "
356 L
"Management\\PrefetchParameters";
357 static const DWORD SUPERFETCH_DEFAULT_PARAM
= 3;
358 static const DWORD PREFETCH_DEFAULT_PARAM
= 3;
360 HKEY prefetchParamsHKey
;
362 LONG prefetchParamsStatus
=
363 RegOpenKeyExW(HKEY_LOCAL_MACHINE
, prefetchParamsKeyName
, 0,
364 KEY_READ
| KEY_WOW64_64KEY
, &prefetchParamsHKey
);
366 if (prefetchParamsStatus
== ERROR_SUCCESS
) {
367 DWORD valueSize
= sizeof(DWORD
);
368 DWORD superfetchValue
= 0;
369 nsAutoRegKey
prefetchParamsKey(prefetchParamsHKey
);
370 LONG superfetchParamStatus
= RegQueryValueExW(
371 prefetchParamsHKey
, L
"EnableSuperfetch", nullptr, &type
,
372 reinterpret_cast<LPBYTE
>(&superfetchValue
), &valueSize
);
374 // If the EnableSuperfetch registry key doesn't exist, then it's using the
375 // default configuration.
376 if (superfetchParamStatus
== ERROR_SUCCESS
&&
377 superfetchValue
!= SUPERFETCH_DEFAULT_PARAM
) {
378 superfetchUsingDefaultParams
= false;
381 DWORD prefetchValue
= 0;
383 LONG prefetchParamStatus
= RegQueryValueExW(
384 prefetchParamsHKey
, L
"EnablePrefetcher", nullptr, &type
,
385 reinterpret_cast<LPBYTE
>(&prefetchValue
), &valueSize
);
387 // If the EnablePrefetcher registry key doesn't exist, then we interpret
388 // that as the Prefetcher being disabled (since Prefetch behaviour when
389 // the key is not available appears to be undefined).
390 if (prefetchParamStatus
!= ERROR_SUCCESS
||
391 prefetchValue
!= PREFETCH_DEFAULT_PARAM
) {
392 prefetchUsingDefaultParams
= false;
396 info
.hasSuperfetch
= superfetchServiceRunning
&& superfetchUsingDefaultParams
;
397 info
.hasPrefetch
= prefetchUsingDefaultParams
;
402 nsresult
CollectCountryCode(nsAString
& aCountryCode
) {
403 GEOID geoid
= GetUserGeoID(GEOCLASS_NATION
);
404 if (geoid
== GEOID_NOT_AVAILABLE
) {
405 return NS_ERROR_NOT_AVAILABLE
;
407 // Get required length
408 int numChars
= GetGeoInfoW(geoid
, GEO_ISO2
, nullptr, 0, 0);
410 return NS_ERROR_FAILURE
;
412 // Now get the string for real
413 aCountryCode
.SetLength(numChars
);
415 GetGeoInfoW(geoid
, GEO_ISO2
, char16ptr_t(aCountryCode
.BeginWriting()),
416 aCountryCode
.Length(), 0);
418 return NS_ERROR_FAILURE
;
421 // numChars includes null terminator
422 aCountryCode
.Truncate(numChars
- 1);
430 static HRESULT
EnumWSCProductList(
431 nsAString
& aOutput
, mozilla::NotNull
<IWSCProductList
*> aProdList
) {
432 MOZ_ASSERT(aOutput
.IsEmpty());
435 HRESULT hr
= aProdList
->get_Count(&count
);
440 for (LONG index
= 0; index
< count
; ++index
) {
441 RefPtr
<IWscProduct
> product
;
442 hr
= aProdList
->get_Item(index
, getter_AddRefs(product
));
447 WSC_SECURITY_PRODUCT_STATE state
;
448 hr
= product
->get_ProductState(&state
);
453 // We only care about products that are active
454 if (state
== WSC_SECURITY_PRODUCT_STATE_OFF
||
455 state
== WSC_SECURITY_PRODUCT_STATE_SNOOZED
) {
460 hr
= product
->get_ProductName(bName
.GetAddress());
465 if (!aOutput
.IsEmpty()) {
466 aOutput
.AppendLiteral(u
";");
469 aOutput
.Append((wchar_t*)bName
, bName
.length());
475 static nsresult
GetWindowsSecurityCenterInfo(nsAString
& aAVInfo
,
476 nsAString
& aAntiSpyInfo
,
477 nsAString
& aFirewallInfo
) {
479 aAntiSpyInfo
.Truncate();
480 aFirewallInfo
.Truncate();
482 if (!XRE_IsParentProcess()) {
483 return NS_ERROR_NOT_AVAILABLE
;
486 const CLSID clsid
= __uuidof(WSCProductList
);
487 const IID iid
= __uuidof(IWSCProductList
);
489 // NB: A separate instance of IWSCProductList is needed for each distinct
490 // security provider type; MSDN says that we cannot reuse the same object
491 // and call Initialize() to pave over the previous data.
493 WSC_SECURITY_PROVIDER providerTypes
[] = {WSC_SECURITY_PROVIDER_ANTIVIRUS
,
494 WSC_SECURITY_PROVIDER_ANTISPYWARE
,
495 WSC_SECURITY_PROVIDER_FIREWALL
};
497 // Each output must match the corresponding entry in providerTypes.
498 nsAString
* outputs
[] = {&aAVInfo
, &aAntiSpyInfo
, &aFirewallInfo
};
501 mozilla::ArrayLength(providerTypes
) == mozilla::ArrayLength(outputs
),
502 "Length of providerTypes and outputs arrays must match");
504 for (uint32_t index
= 0; index
< mozilla::ArrayLength(providerTypes
);
506 RefPtr
<IWSCProductList
> prodList
;
507 HRESULT hr
= ::CoCreateInstance(clsid
, nullptr, CLSCTX_INPROC_SERVER
, iid
,
508 getter_AddRefs(prodList
));
510 return NS_ERROR_NOT_AVAILABLE
;
513 hr
= prodList
->Initialize(providerTypes
[index
]);
515 return NS_ERROR_UNEXPECTED
;
518 hr
= EnumWSCProductList(*outputs
[index
],
519 mozilla::WrapNotNull(prodList
.get()));
521 return NS_ERROR_UNEXPECTED
;
528 # endif // __MINGW32__
530 #endif // defined(XP_WIN)
533 static nsresult
GetAppleModelId(nsAutoCString
& aModelId
) {
535 size_t result
= sysctlbyname("hw.model", nullptr, &numChars
, nullptr, 0);
536 if (result
!= 0 || !numChars
) {
537 return NS_ERROR_FAILURE
;
539 aModelId
.SetLength(numChars
);
541 sysctlbyname("hw.model", aModelId
.BeginWriting(), &numChars
, nullptr, 0);
543 return NS_ERROR_FAILURE
;
545 // numChars includes null terminator
546 aModelId
.Truncate(numChars
- 1);
550 static nsresult
ProcessIsRosettaTranslated(bool& isRosetta
) {
551 # if defined(__aarch64__)
552 // There is no need to call sysctlbyname() if we are running as arm64.
556 size_t size
= sizeof(ret
);
557 if (sysctlbyname("sysctl.proc_translated", &ret
, &size
, NULL
, 0) == -1) {
558 if (errno
!= ENOENT
) {
559 fprintf(stderr
, "Failed to check for translation environment\n");
563 isRosetta
= (ret
== 1);
570 using namespace mozilla
;
572 nsSystemInfo::nsSystemInfo() = default;
574 nsSystemInfo::~nsSystemInfo() = default;
576 // CPU-specific information.
577 static const struct PropItems
{
579 bool (*propfun
)(void);
581 // x86-specific bits.
582 {"hasMMX", mozilla::supports_mmx
},
583 {"hasSSE", mozilla::supports_sse
},
584 {"hasSSE2", mozilla::supports_sse2
},
585 {"hasSSE3", mozilla::supports_sse3
},
586 {"hasSSSE3", mozilla::supports_ssse3
},
587 {"hasSSE4A", mozilla::supports_sse4a
},
588 {"hasSSE4_1", mozilla::supports_sse4_1
},
589 {"hasSSE4_2", mozilla::supports_sse4_2
},
590 {"hasAVX", mozilla::supports_avx
},
591 {"hasAVX2", mozilla::supports_avx2
},
592 {"hasAES", mozilla::supports_aes
},
593 // ARM-specific bits.
594 {"hasEDSP", mozilla::supports_edsp
},
595 {"hasARMv6", mozilla::supports_armv6
},
596 {"hasARMv7", mozilla::supports_armv7
},
597 {"hasNEON", mozilla::supports_neon
}};
599 nsresult
CollectProcessInfo(ProcessInfo
& info
) {
600 nsAutoCString cpuVendor
;
601 nsAutoCString cpuName
;
605 int cpuStepping
= -1;
606 int logicalCPUs
= -1;
607 int physicalCPUs
= -1;
608 int cacheSizeL2
= -1;
609 int cacheSizeL3
= -1;
612 // IsWow64Process2 is only available on Windows 10+, so we have to dynamically
613 // check for its existence.
614 typedef BOOL(WINAPI
* LPFN_IWP2
)(HANDLE
, USHORT
*, USHORT
*);
615 LPFN_IWP2 iwp2
= reinterpret_cast<LPFN_IWP2
>(
616 GetProcAddress(GetModuleHandle(L
"kernel32"), "IsWow64Process2"));
617 BOOL isWow64
= FALSE
;
618 USHORT processMachine
= IMAGE_FILE_MACHINE_UNKNOWN
;
619 USHORT nativeMachine
= IMAGE_FILE_MACHINE_UNKNOWN
;
622 gotWow64Value
= iwp2(GetCurrentProcess(), &processMachine
, &nativeMachine
);
624 isWow64
= (processMachine
!= IMAGE_FILE_MACHINE_UNKNOWN
);
627 gotWow64Value
= IsWow64Process(GetCurrentProcess(), &isWow64
);
628 // The function only indicates a WOW64 environment if it's 32-bit x86
629 // running on x86-64, so emulate what IsWow64Process2 would have given.
630 if (gotWow64Value
&& isWow64
) {
631 processMachine
= IMAGE_FILE_MACHINE_I386
;
632 nativeMachine
= IMAGE_FILE_MACHINE_AMD64
;
635 NS_WARNING_ASSERTION(gotWow64Value
, "IsWow64Process failed");
637 // Set this always, even for the x86-on-arm64 case.
638 info
.isWow64
= !!isWow64
;
639 // Additional information if we're running x86-on-arm64
640 info
.isWowARM64
= (processMachine
== IMAGE_FILE_MACHINE_I386
&&
641 nativeMachine
== IMAGE_FILE_MACHINE_ARM64
);
647 // WindowsIntegrityPolicy is only available on newer versions
648 // of Windows 10, so there's no point in trying to check this
649 // on earlier versions. We know GetActivationFactory crashes on
650 // Windows 7 when trying to retrieve this class, and may also
651 // crash on very old versions of Windows 10.
652 if (IsWin10Sep2018UpdateOrLater()) {
653 ComPtr
<IWindowsIntegrityPolicyStatics
> wip
;
654 HRESULT hr
= GetActivationFactory(
656 RuntimeClass_Windows_System_Profile_WindowsIntegrityPolicy
)
660 // info.isWindowsSMode ends up true if Windows is in S mode, otherwise
662 // https://docs.microsoft.com/en-us/uwp/api/windows.system.profile.windowsintegritypolicy.isenabled?view=winrt-22000
663 hr
= wip
->get_IsEnabled(&info
.isWindowsSMode
);
664 NS_WARNING_ASSERTION(SUCCEEDED(hr
),
665 "WindowsIntegrityPolicy.IsEnabled failed");
668 # endif // __MINGW32__
672 static const WCHAR keyName
[] =
673 L
"HARDWARE\\DESCRIPTION\\System\\CentralProcessor\\0";
675 if (RegOpenKeyEx(HKEY_LOCAL_MACHINE
, keyName
, 0, KEY_QUERY_VALUE
, &key
) ==
677 DWORD data
, len
, vtype
;
680 if (RegQueryValueEx(key
, L
"~Mhz", 0, 0, reinterpret_cast<LPBYTE
>(&data
),
681 &len
) == ERROR_SUCCESS
) {
682 cpuSpeed
= static_cast<int>(data
);
685 // Limit to 64 double byte characters, should be plenty, but create
686 // a buffer one larger as the result may not be null terminated. If
687 // it is more than 64, we will not get the value.
688 wchar_t cpuVendorStr
[64 + 1];
689 len
= sizeof(cpuVendorStr
) - 2;
690 if (RegQueryValueExW(key
, L
"VendorIdentifier", 0, &vtype
,
691 reinterpret_cast<LPBYTE
>(cpuVendorStr
),
692 &len
) == ERROR_SUCCESS
&&
693 vtype
== REG_SZ
&& len
% 2 == 0 && len
> 1) {
694 cpuVendorStr
[len
/ 2] = 0; // In case it isn't null terminated
695 CopyUTF16toUTF8(nsDependentString(cpuVendorStr
), cpuVendor
);
698 // Limit to 64 double byte characters, should be plenty, but create
699 // a buffer one larger as the result may not be null terminated. If
700 // it is more than 64, we will not get the value.
701 // The expected string size is 48 characters or less.
702 wchar_t cpuNameStr
[64 + 1];
703 len
= sizeof(cpuNameStr
) - 2;
704 if (RegQueryValueExW(key
, L
"ProcessorNameString", 0, &vtype
,
705 reinterpret_cast<LPBYTE
>(cpuNameStr
),
706 &len
) == ERROR_SUCCESS
&&
707 vtype
== REG_SZ
&& len
% 2 == 0 && len
> 1) {
708 cpuNameStr
[len
/ 2] = 0; // In case it isn't null terminated
709 CopyUTF16toUTF8(nsDependentString(cpuNameStr
), cpuName
);
715 // Other CPU attributes:
717 GetNativeSystemInfo(&si
);
718 logicalCPUs
= si
.dwNumberOfProcessors
;
719 GetProcessorInformation(&physicalCPUs
, &cacheSizeL2
, &cacheSizeL3
);
720 if (physicalCPUs
<= 0) {
721 physicalCPUs
= logicalCPUs
;
723 cpuFamily
= si
.wProcessorLevel
;
724 cpuModel
= si
.wProcessorRevision
>> 8;
725 cpuStepping
= si
.wProcessorRevision
& 0xFF;
726 #elif defined(XP_MACOSX)
728 uint64_t sysctlValue64
= 0;
729 uint32_t sysctlValue32
= 0;
731 len
= sizeof(sysctlValue64
);
732 if (!sysctlbyname("hw.cpufrequency_max", &sysctlValue64
, &len
, NULL
, 0)) {
733 cpuSpeed
= static_cast<int>(sysctlValue64
/ 1000000);
735 MOZ_ASSERT(sizeof(sysctlValue64
) == len
);
737 len
= sizeof(sysctlValue32
);
738 if (!sysctlbyname("hw.physicalcpu_max", &sysctlValue32
, &len
, NULL
, 0)) {
739 physicalCPUs
= static_cast<int>(sysctlValue32
);
741 MOZ_ASSERT(sizeof(sysctlValue32
) == len
);
743 len
= sizeof(sysctlValue32
);
744 if (!sysctlbyname("hw.logicalcpu_max", &sysctlValue32
, &len
, NULL
, 0)) {
745 logicalCPUs
= static_cast<int>(sysctlValue32
);
747 MOZ_ASSERT(sizeof(sysctlValue32
) == len
);
749 len
= sizeof(sysctlValue64
);
750 if (!sysctlbyname("hw.l2cachesize", &sysctlValue64
, &len
, NULL
, 0)) {
751 cacheSizeL2
= static_cast<int>(sysctlValue64
/ 1024);
753 MOZ_ASSERT(sizeof(sysctlValue64
) == len
);
755 len
= sizeof(sysctlValue64
);
756 if (!sysctlbyname("hw.l3cachesize", &sysctlValue64
, &len
, NULL
, 0)) {
757 cacheSizeL3
= static_cast<int>(sysctlValue64
/ 1024);
759 MOZ_ASSERT(sizeof(sysctlValue64
) == len
);
761 if (!sysctlbyname("machdep.cpu.vendor", NULL
, &len
, NULL
, 0)) {
762 char* cpuVendorStr
= new char[len
];
763 if (!sysctlbyname("machdep.cpu.vendor", cpuVendorStr
, &len
, NULL
, 0)) {
764 cpuVendor
= cpuVendorStr
;
766 delete[] cpuVendorStr
;
769 if (!sysctlbyname("machdep.cpu.brand_string", NULL
, &len
, NULL
, 0)) {
770 char* cpuNameStr
= new char[len
];
771 if (!sysctlbyname("machdep.cpu.brand_string", cpuNameStr
, &len
, NULL
, 0)) {
772 cpuName
= cpuNameStr
;
777 len
= sizeof(sysctlValue32
);
778 if (!sysctlbyname("machdep.cpu.family", &sysctlValue32
, &len
, NULL
, 0)) {
779 cpuFamily
= static_cast<int>(sysctlValue32
);
781 MOZ_ASSERT(sizeof(sysctlValue32
) == len
);
783 len
= sizeof(sysctlValue32
);
784 if (!sysctlbyname("machdep.cpu.model", &sysctlValue32
, &len
, NULL
, 0)) {
785 cpuModel
= static_cast<int>(sysctlValue32
);
787 MOZ_ASSERT(sizeof(sysctlValue32
) == len
);
789 len
= sizeof(sysctlValue32
);
790 if (!sysctlbyname("machdep.cpu.stepping", &sysctlValue32
, &len
, NULL
, 0)) {
791 cpuStepping
= static_cast<int>(sysctlValue32
);
793 MOZ_ASSERT(sizeof(sysctlValue32
) == len
);
795 #elif defined(XP_LINUX) && !defined(ANDROID)
796 // Get vendor, family, model, stepping, physical cores
797 // from /proc/cpuinfo file
799 std::map
<nsCString
, nsCString
> keyValuePairs
;
800 SimpleParseKeyValuePairs("/proc/cpuinfo", keyValuePairs
);
802 // cpuVendor from "vendor_id"
803 info
.cpuVendor
.Assign(keyValuePairs
["vendor_id"_ns
]);
805 // cpuName from "model name"
806 info
.cpuName
.Assign(keyValuePairs
["model name"_ns
]);
809 // cpuFamily from "cpu family"
811 Tokenizer
p(keyValuePairs
["cpu family"_ns
]);
812 if (p
.Next(t
) && t
.Type() == Tokenizer::TOKEN_INTEGER
&&
813 t
.AsInteger() <= INT32_MAX
) {
814 cpuFamily
= static_cast<int32_t>(t
.AsInteger());
819 // cpuModel from "model"
821 Tokenizer
p(keyValuePairs
["model"_ns
]);
822 if (p
.Next(t
) && t
.Type() == Tokenizer::TOKEN_INTEGER
&&
823 t
.AsInteger() <= INT32_MAX
) {
824 cpuModel
= static_cast<int32_t>(t
.AsInteger());
829 // cpuStepping from "stepping"
831 Tokenizer
p(keyValuePairs
["stepping"_ns
]);
832 if (p
.Next(t
) && t
.Type() == Tokenizer::TOKEN_INTEGER
&&
833 t
.AsInteger() <= INT32_MAX
) {
834 cpuStepping
= static_cast<int32_t>(t
.AsInteger());
839 // physicalCPUs from "cpu cores"
841 Tokenizer
p(keyValuePairs
["cpu cores"_ns
]);
842 if (p
.Next(t
) && t
.Type() == Tokenizer::TOKEN_INTEGER
&&
843 t
.AsInteger() <= INT32_MAX
) {
844 physicalCPUs
= static_cast<int32_t>(t
.AsInteger());
850 // Get cpuSpeed from another file.
852 "/sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_max_freq");
854 if (getline(input
, line
)) {
856 Tokenizer
p(line
.c_str());
857 if (p
.Next(t
) && t
.Type() == Tokenizer::TOKEN_INTEGER
&&
858 t
.AsInteger() <= INT32_MAX
) {
859 cpuSpeed
= static_cast<int32_t>(t
.AsInteger() / 1000);
865 // Get cacheSizeL2 from yet another file
866 std::ifstream
input("/sys/devices/system/cpu/cpu0/cache/index2/size");
868 if (getline(input
, line
)) {
870 Tokenizer
p(line
.c_str(), nullptr, "K");
871 if (p
.Next(t
) && t
.Type() == Tokenizer::TOKEN_INTEGER
&&
872 t
.AsInteger() <= INT32_MAX
) {
873 cacheSizeL2
= static_cast<int32_t>(t
.AsInteger());
879 // Get cacheSizeL3 from yet another file
880 std::ifstream
input("/sys/devices/system/cpu/cpu0/cache/index3/size");
882 if (getline(input
, line
)) {
884 Tokenizer
p(line
.c_str(), nullptr, "K");
885 if (p
.Next(t
) && t
.Type() == Tokenizer::TOKEN_INTEGER
&&
886 t
.AsInteger() <= INT32_MAX
) {
887 cacheSizeL3
= static_cast<int32_t>(t
.AsInteger());
892 info
.cpuCount
= PR_GetNumberOfProcessors();
894 info
.cpuCount
= PR_GetNumberOfProcessors();
898 info
.cpuSpeed
= cpuSpeed
;
902 if (!cpuVendor
.IsEmpty()) {
903 info
.cpuVendor
= cpuVendor
;
905 if (!cpuName
.IsEmpty()) {
906 info
.cpuName
= cpuName
;
908 if (cpuFamily
>= 0) {
909 info
.cpuFamily
= cpuFamily
;
912 info
.cpuModel
= cpuModel
;
914 if (cpuStepping
>= 0) {
915 info
.cpuStepping
= cpuStepping
;
918 if (logicalCPUs
>= 0) {
919 info
.cpuCount
= logicalCPUs
;
921 if (physicalCPUs
>= 0) {
922 info
.cpuCores
= physicalCPUs
;
925 if (cacheSizeL2
>= 0) {
926 info
.l2cacheKB
= cacheSizeL2
;
928 if (cacheSizeL3
>= 0) {
929 info
.l3cacheKB
= cacheSizeL3
;
935 #if defined(__MINGW32__)
937 BOOL WINAPI
IsUserCetAvailableInEnvironment(_In_ DWORD UserCetEnvironment
);
939 # define USER_CET_ENVIRONMENT_WIN32_PROCESS 0x00000000
942 nsresult
nsSystemInfo::Init() {
943 // check that it is called from the main thread on all platforms.
944 MOZ_ASSERT(NS_IsMainThread());
948 static const struct {
951 } items
[] = {{PR_SI_SYSNAME
, "name"},
952 {PR_SI_ARCHITECTURE
, "arch"},
953 {PR_SI_RELEASE
, "version"},
954 {PR_SI_RELEASE_BUILD
, "build"}};
956 for (uint32_t i
= 0; i
< (sizeof(items
) / sizeof(items
[0])); i
++) {
957 char buf
[SYS_INFO_BUFFER_LENGTH
];
958 if (PR_GetSystemInfo(items
[i
].cmd
, buf
, sizeof(buf
)) == PR_SUCCESS
) {
959 rv
= SetPropertyAsACString(NS_ConvertASCIItoUTF16(items
[i
].name
),
960 nsDependentCString(buf
));
961 if (NS_WARN_IF(NS_FAILED(rv
))) {
965 NS_WARNING("PR_GetSystemInfo failed");
969 SetPropertyAsBool(u
"isPackagedApp"_ns
, false);
971 // Additional informations not available through PR_GetSystemInfo.
972 SetInt32Property(u
"pagesize"_ns
, PR_GetPageSize());
973 SetInt32Property(u
"pageshift"_ns
, PR_GetPageShift());
974 SetInt32Property(u
"memmapalign"_ns
, PR_GetMemMapAlignment());
975 SetUint64Property(u
"memsize"_ns
, PR_GetPhysicalMemorySize());
976 SetUint32Property(u
"umask"_ns
, nsSystemInfo::gUserUmask
);
978 #ifdef HAVE_64BIT_BUILD
979 SetUint32Property(u
"archbits"_ns
, 64);
981 SetUint32Property(u
"archbits"_ns
, 32);
984 uint64_t virtualMem
= 0;
988 MEMORYSTATUSEX memStat
;
989 memStat
.dwLength
= sizeof(memStat
);
990 if (GlobalMemoryStatusEx(&memStat
)) {
991 virtualMem
= memStat
.ullTotalVirtual
;
994 if (virtualMem
) SetUint64Property(u
"virtualmemsize"_ns
, virtualMem
);
996 for (uint32_t i
= 0; i
< ArrayLength(cpuPropItems
); i
++) {
997 rv
= SetPropertyAsBool(NS_ConvertASCIItoUTF16(cpuPropItems
[i
].name
),
998 cpuPropItems
[i
].propfun());
999 if (NS_WARN_IF(NS_FAILED(rv
))) {
1011 rv
= SetPropertyAsBool(u
"isMinGW"_ns
, !!isMinGW
);
1012 if (NS_WARN_IF(NS_FAILED(rv
))) {
1016 boolean hasPackageIdentity
= widget::WinUtils::HasPackageIdentity();
1018 rv
= SetPropertyAsBool(u
"hasWinPackageId"_ns
, hasPackageIdentity
);
1019 if (NS_WARN_IF(NS_FAILED(rv
))) {
1023 rv
= SetPropertyAsAString(u
"winPackageFamilyName"_ns
,
1024 widget::WinUtils::GetPackageFamilyName());
1025 if (NS_WARN_IF(NS_FAILED(rv
))) {
1029 rv
= SetPropertyAsBool(u
"isPackagedApp"_ns
, hasPackageIdentity
);
1030 if (NS_WARN_IF(NS_FAILED(rv
))) {
1034 # ifndef __MINGW32__
1035 nsAutoString avInfo
, antiSpyInfo
, firewallInfo
;
1037 GetWindowsSecurityCenterInfo(avInfo
, antiSpyInfo
, firewallInfo
))) {
1038 if (!avInfo
.IsEmpty()) {
1039 rv
= SetPropertyAsAString(u
"registeredAntiVirus"_ns
, avInfo
);
1040 if (NS_WARN_IF(NS_FAILED(rv
))) {
1045 if (!antiSpyInfo
.IsEmpty()) {
1046 rv
= SetPropertyAsAString(u
"registeredAntiSpyware"_ns
, antiSpyInfo
);
1047 if (NS_WARN_IF(NS_FAILED(rv
))) {
1052 if (!firewallInfo
.IsEmpty()) {
1053 rv
= SetPropertyAsAString(u
"registeredFirewall"_ns
, firewallInfo
);
1054 if (NS_WARN_IF(NS_FAILED(rv
))) {
1059 # endif // __MINGW32__
1061 mozilla::DynamicallyLinkedFunctionPtr
<
1062 decltype(&IsUserCetAvailableInEnvironment
)>
1063 isUserCetAvailable(L
"api-ms-win-core-sysinfo-l1-2-6.dll",
1064 "IsUserCetAvailableInEnvironment");
1065 bool hasUserCET
= isUserCetAvailable
&&
1066 isUserCetAvailable(USER_CET_ENVIRONMENT_WIN32_PROCESS
);
1067 rv
= SetPropertyAsBool(u
"hasUserCET"_ns
, hasUserCET
);
1068 if (NS_WARN_IF(NS_FAILED(rv
))) {
1072 nsString pointerExplanation
;
1073 widget::WinUtils::GetPointerExplanation(&pointerExplanation
);
1074 rv
= SetPropertyAsAString(u
"pointingDevices"_ns
, pointerExplanation
);
1075 if (NS_WARN_IF(NS_FAILED(rv
))) {
1081 #if defined(XP_MACOSX)
1082 nsAutoCString modelId
;
1083 if (NS_SUCCEEDED(GetAppleModelId(modelId
))) {
1084 rv
= SetPropertyAsACString(u
"appleModelId"_ns
, modelId
);
1085 NS_ENSURE_SUCCESS(rv
, rv
);
1088 if (NS_SUCCEEDED(ProcessIsRosettaTranslated(isRosetta
))) {
1089 rv
= SetPropertyAsBool(u
"rosettaStatus"_ns
, isRosetta
);
1090 NS_ENSURE_SUCCESS(rv
, rv
);
1095 nsAutoCString themeInfo
;
1096 LookAndFeel::GetThemeInfo(themeInfo
);
1097 MOZ_TRY(SetPropertyAsACString(u
"osThemeInfo"_ns
, themeInfo
));
1100 #if defined(MOZ_WIDGET_GTK)
1101 // This must be done here because NSPR can only separate OS's when compiled,
1102 // not libraries. 64 bytes is going to be well enough for "GTK " followed by 3
1103 // integers separated with dots.
1105 ssize_t gtkver_len
= 0;
1107 if (gtkver_len
<= 0) {
1108 gtkver_len
= SprintfLiteral(gtkver
, "GTK %u.%u.%u", gtk_major_version
,
1109 gtk_minor_version
, gtk_micro_version
);
1112 nsAutoCString secondaryLibrary
;
1113 if (gtkver_len
> 0 && gtkver_len
< int(sizeof(gtkver
))) {
1114 secondaryLibrary
.Append(nsDependentCSubstring(gtkver
, gtkver_len
));
1118 // With TSan, avoid loading libpulse here because we cannot unload it
1119 // afterwards due to restrictions from TSan about unloading libraries
1120 // matched by the suppression list.
1121 void* libpulse
= dlopen("libpulse.so.0", RTLD_LAZY
);
1122 const char* libpulseVersion
= "not-available";
1124 auto pa_get_library_version
= reinterpret_cast<const char* (*)()>(
1125 dlsym(libpulse
, "pa_get_library_version"));
1127 if (pa_get_library_version
) {
1128 libpulseVersion
= pa_get_library_version();
1132 secondaryLibrary
.AppendPrintf(",libpulse %s", libpulseVersion
);
1139 rv
= SetPropertyAsACString(u
"secondaryLibrary"_ns
, secondaryLibrary
);
1140 if (NS_WARN_IF(NS_FAILED(rv
))) {
1143 rv
= SetPropertyAsBool(u
"isPackagedApp"_ns
,
1144 widget::IsRunningUnderFlatpakOrSnap() ||
1145 widget::IsPackagedAppFileExists());
1146 if (NS_WARN_IF(NS_FAILED(rv
))) {
1151 #ifdef MOZ_WIDGET_ANDROID
1152 AndroidSystemInfo info
;
1153 GetAndroidSystemInfo(&info
);
1154 SetupAndroidInfo(info
);
1157 #if defined(XP_LINUX) && !defined(ANDROID)
1158 nsCString dist
, desc
, release
, codename
;
1159 if (widget::lsb::GetLSBRelease(dist
, desc
, release
, codename
)) {
1160 SetPropertyAsACString(u
"distro"_ns
, dist
);
1161 SetPropertyAsACString(u
"distroVersion"_ns
, release
);
1165 #if defined(XP_LINUX) && defined(MOZ_SANDBOX)
1166 SandboxInfo sandInfo
= SandboxInfo::Get();
1168 SetPropertyAsBool(u
"hasSeccompBPF"_ns
,
1169 sandInfo
.Test(SandboxInfo::kHasSeccompBPF
));
1170 SetPropertyAsBool(u
"hasSeccompTSync"_ns
,
1171 sandInfo
.Test(SandboxInfo::kHasSeccompTSync
));
1172 SetPropertyAsBool(u
"hasUserNamespaces"_ns
,
1173 sandInfo
.Test(SandboxInfo::kHasUserNamespaces
));
1174 SetPropertyAsBool(u
"hasPrivilegedUserNamespaces"_ns
,
1175 sandInfo
.Test(SandboxInfo::kHasPrivilegedUserNamespaces
));
1177 if (sandInfo
.Test(SandboxInfo::kEnabledForContent
)) {
1178 SetPropertyAsBool(u
"canSandboxContent"_ns
, sandInfo
.CanSandboxContent());
1181 if (sandInfo
.Test(SandboxInfo::kEnabledForMedia
)) {
1182 SetPropertyAsBool(u
"canSandboxMedia"_ns
, sandInfo
.CanSandboxMedia());
1184 #endif // XP_LINUX && MOZ_SANDBOX
1189 #ifdef MOZ_WIDGET_ANDROID
1190 // Prerelease versions of Android use a letter instead of version numbers.
1191 // Unfortunately this breaks websites due to the user agent.
1192 // Chrome works around this by hardcoding an Android version when a
1193 // numeric version can't be obtained. We're doing the same.
1194 // This version will need to be updated whenever there is a new official
1195 // Android release. Search for "kDefaultAndroidMajorVersion" in:
1196 // https://source.chromium.org/chromium/chromium/src/+/master:base/system/sys_info_android.cc
1197 # define DEFAULT_ANDROID_VERSION u"10.0.99"
1200 void nsSystemInfo::GetAndroidSystemInfo(AndroidSystemInfo
* aInfo
) {
1201 if (!jni::IsAvailable()) {
1202 // called from xpcshell etc.
1203 aInfo
->sdk_version() = 0;
1207 jni::String::LocalRef model
= java::sdk::Build::MODEL();
1208 aInfo
->device() = model
->ToString();
1210 jni::String::LocalRef manufacturer
=
1211 mozilla::java::sdk::Build::MANUFACTURER();
1212 aInfo
->manufacturer() = manufacturer
->ToString();
1214 jni::String::LocalRef hardware
= java::sdk::Build::HARDWARE();
1215 aInfo
->hardware() = hardware
->ToString();
1217 jni::String::LocalRef release
= java::sdk::Build::VERSION::RELEASE();
1218 nsString
str(release
->ToString());
1222 int num_read
= sscanf(NS_ConvertUTF16toUTF8(str
).get(), "%d.%d.%d",
1223 &major_version
, &minor_version
, &bugfix_version
);
1224 if (num_read
== 0) {
1225 aInfo
->release_version() = nsLiteralString(DEFAULT_ANDROID_VERSION
);
1227 aInfo
->release_version() = str
;
1230 aInfo
->sdk_version() = jni::GetAPIVersion();
1231 aInfo
->isTablet() = java::GeckoAppShell::IsTablet();
1234 void nsSystemInfo::SetupAndroidInfo(const AndroidSystemInfo
& aInfo
) {
1235 if (!aInfo
.device().IsEmpty()) {
1236 SetPropertyAsAString(u
"device"_ns
, aInfo
.device());
1238 if (!aInfo
.manufacturer().IsEmpty()) {
1239 SetPropertyAsAString(u
"manufacturer"_ns
, aInfo
.manufacturer());
1241 if (!aInfo
.release_version().IsEmpty()) {
1242 SetPropertyAsAString(u
"release_version"_ns
, aInfo
.release_version());
1244 SetPropertyAsBool(u
"tablet"_ns
, aInfo
.isTablet());
1245 // NSPR "version" is the kernel version. For Android we want the Android
1246 // version. Rename SDK version to version and put the kernel version into
1249 nsresult rv
= GetPropertyAsAString(u
"version"_ns
, str
);
1250 if (NS_SUCCEEDED(rv
)) {
1251 SetPropertyAsAString(u
"kernel_version"_ns
, str
);
1253 // When JNI is not available (eg. in xpcshell tests), sdk_version is 0.
1254 if (aInfo
.sdk_version() != 0) {
1255 if (!aInfo
.hardware().IsEmpty()) {
1256 SetPropertyAsAString(u
"hardware"_ns
, aInfo
.hardware());
1258 SetPropertyAsInt32(u
"version"_ns
, aInfo
.sdk_version());
1261 #endif // MOZ_WIDGET_ANDROID
1263 void nsSystemInfo::SetInt32Property(const nsAString
& aPropertyName
,
1264 const int32_t aValue
) {
1265 NS_WARNING_ASSERTION(aValue
> 0, "Unable to read system value");
1270 SetPropertyAsInt32(aPropertyName
, aValue
);
1271 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv
), "Unable to set property");
1275 void nsSystemInfo::SetUint32Property(const nsAString
& aPropertyName
,
1276 const uint32_t aValue
) {
1277 // Only one property is currently set via this function.
1278 // It may legitimately be zero.
1282 SetPropertyAsUint32(aPropertyName
, aValue
);
1283 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv
), "Unable to set property");
1286 void nsSystemInfo::SetUint64Property(const nsAString
& aPropertyName
,
1287 const uint64_t aValue
) {
1288 NS_WARNING_ASSERTION(aValue
> 0, "Unable to read system value");
1293 SetPropertyAsUint64(aPropertyName
, aValue
);
1294 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv
), "Unable to set property");
1300 static bool GetJSObjForDiskInfo(JSContext
* aCx
, JS::Handle
<JSObject
*> aParent
,
1301 const FolderDiskInfo
& info
,
1302 const char* propName
) {
1303 JS::Rooted
<JSObject
*> jsInfo(aCx
, JS_NewPlainObject(aCx
));
1308 JSString
* strModel
=
1309 JS_NewStringCopyN(aCx
, info
.model
.get(), info
.model
.Length());
1313 JS::Rooted
<JS::Value
> valModel(aCx
, JS::StringValue(strModel
));
1314 if (!JS_SetProperty(aCx
, jsInfo
, "model", valModel
)) {
1318 JSString
* strRevision
=
1319 JS_NewStringCopyN(aCx
, info
.revision
.get(), info
.revision
.Length());
1323 JS::Rooted
<JS::Value
> valRevision(aCx
, JS::StringValue(strRevision
));
1324 if (!JS_SetProperty(aCx
, jsInfo
, "revision", valRevision
)) {
1328 JSString
* strSSD
= JS_NewStringCopyZ(aCx
, info
.isSSD
? "SSD" : "HDD");
1332 JS::Rooted
<JS::Value
> valSSD(aCx
, JS::StringValue(strSSD
));
1333 if (!JS_SetProperty(aCx
, jsInfo
, "type", valSSD
)) {
1337 JS::Rooted
<JS::Value
> val(aCx
, JS::ObjectValue(*jsInfo
));
1338 return JS_SetProperty(aCx
, aParent
, propName
, val
);
1341 JSObject
* GetJSObjForOSInfo(JSContext
* aCx
, const OSInfo
& info
) {
1342 JS::Rooted
<JSObject
*> jsInfo(aCx
, JS_NewPlainObject(aCx
));
1344 JS::Rooted
<JS::Value
> valInstallYear(aCx
, JS::Int32Value(info
.installYear
));
1345 JS_SetProperty(aCx
, jsInfo
, "installYear", valInstallYear
);
1347 JS::Rooted
<JS::Value
> valHasSuperfetch(aCx
,
1348 JS::BooleanValue(info
.hasSuperfetch
));
1349 JS_SetProperty(aCx
, jsInfo
, "hasSuperfetch", valHasSuperfetch
);
1351 JS::Rooted
<JS::Value
> valHasPrefetch(aCx
, JS::BooleanValue(info
.hasPrefetch
));
1352 JS_SetProperty(aCx
, jsInfo
, "hasPrefetch", valHasPrefetch
);
1359 JSObject
* GetJSObjForProcessInfo(JSContext
* aCx
, const ProcessInfo
& info
) {
1360 JS::Rooted
<JSObject
*> jsInfo(aCx
, JS_NewPlainObject(aCx
));
1363 JS::Rooted
<JS::Value
> valisWow64(aCx
, JS::BooleanValue(info
.isWow64
));
1364 JS_SetProperty(aCx
, jsInfo
, "isWow64", valisWow64
);
1366 JS::Rooted
<JS::Value
> valisWowARM64(aCx
, JS::BooleanValue(info
.isWowARM64
));
1367 JS_SetProperty(aCx
, jsInfo
, "isWowARM64", valisWowARM64
);
1369 JS::Rooted
<JS::Value
> valisWindowsSMode(
1370 aCx
, JS::BooleanValue(info
.isWindowsSMode
));
1371 JS_SetProperty(aCx
, jsInfo
, "isWindowsSMode", valisWindowsSMode
);
1374 JS::Rooted
<JS::Value
> valCountInfo(aCx
, JS::Int32Value(info
.cpuCount
));
1375 JS_SetProperty(aCx
, jsInfo
, "count", valCountInfo
);
1377 JS::Rooted
<JS::Value
> valCoreInfo(aCx
, JS::Int32Value(info
.cpuCores
));
1378 JS_SetProperty(aCx
, jsInfo
, "cores", valCoreInfo
);
1380 JSString
* strVendor
=
1381 JS_NewStringCopyN(aCx
, info
.cpuVendor
.get(), info
.cpuVendor
.Length());
1382 JS::Rooted
<JS::Value
> valVendor(aCx
, JS::StringValue(strVendor
));
1383 JS_SetProperty(aCx
, jsInfo
, "vendor", valVendor
);
1386 JS_NewStringCopyN(aCx
, info
.cpuName
.get(), info
.cpuName
.Length());
1387 JS::Rooted
<JS::Value
> valName(aCx
, JS::StringValue(strName
));
1388 JS_SetProperty(aCx
, jsInfo
, "name", valName
);
1390 JS::Rooted
<JS::Value
> valFamilyInfo(aCx
, JS::Int32Value(info
.cpuFamily
));
1391 JS_SetProperty(aCx
, jsInfo
, "family", valFamilyInfo
);
1393 JS::Rooted
<JS::Value
> valModelInfo(aCx
, JS::Int32Value(info
.cpuModel
));
1394 JS_SetProperty(aCx
, jsInfo
, "model", valModelInfo
);
1396 JS::Rooted
<JS::Value
> valSteppingInfo(aCx
, JS::Int32Value(info
.cpuStepping
));
1397 JS_SetProperty(aCx
, jsInfo
, "stepping", valSteppingInfo
);
1399 JS::Rooted
<JS::Value
> valL2CacheInfo(aCx
, JS::Int32Value(info
.l2cacheKB
));
1400 JS_SetProperty(aCx
, jsInfo
, "l2cacheKB", valL2CacheInfo
);
1402 JS::Rooted
<JS::Value
> valL3CacheInfo(aCx
, JS::Int32Value(info
.l3cacheKB
));
1403 JS_SetProperty(aCx
, jsInfo
, "l3cacheKB", valL3CacheInfo
);
1405 JS::Rooted
<JS::Value
> valSpeedInfo(aCx
, JS::Int32Value(info
.cpuSpeed
));
1406 JS_SetProperty(aCx
, jsInfo
, "speedMHz", valSpeedInfo
);
1411 RefPtr
<nsISerialEventTarget
> nsSystemInfo::GetBackgroundTarget() {
1412 if (!mBackgroundET
) {
1413 MOZ_ALWAYS_SUCCEEDS(NS_CreateBackgroundTaskQueue(
1414 "SystemInfoThread", getter_AddRefs(mBackgroundET
)));
1416 return mBackgroundET
;
1420 nsSystemInfo::GetOsInfo(JSContext
* aCx
, Promise
** aResult
) {
1421 NS_ENSURE_ARG_POINTER(aResult
);
1423 if (!XRE_IsParentProcess()) {
1424 return NS_ERROR_FAILURE
;
1427 nsIGlobalObject
* global
= xpc::CurrentNativeGlobal(aCx
);
1428 if (NS_WARN_IF(!global
)) {
1429 return NS_ERROR_FAILURE
;
1433 RefPtr
<Promise
> promise
= Promise::Create(global
, erv
);
1434 if (NS_WARN_IF(erv
.Failed())) {
1435 return erv
.StealNSResult();
1438 if (!mOSInfoPromise
) {
1439 RefPtr
<nsISerialEventTarget
> backgroundET
= GetBackgroundTarget();
1441 mOSInfoPromise
= InvokeAsync(backgroundET
, __func__
, []() {
1443 nsresult rv
= CollectOSInfo(info
);
1444 if (NS_SUCCEEDED(rv
)) {
1445 return OSInfoPromise::CreateAndResolve(info
, __func__
);
1447 return OSInfoPromise::CreateAndReject(rv
, __func__
);
1451 // Chain the new promise to the extant mozpromise
1452 RefPtr
<Promise
> capturedPromise
= promise
;
1453 mOSInfoPromise
->Then(
1454 GetMainThreadSerialEventTarget(), __func__
,
1455 [capturedPromise
](const OSInfo
& info
) {
1457 if (NS_WARN_IF(!jsapi
.Init(capturedPromise
->GetGlobalObject()))) {
1458 capturedPromise
->MaybeReject(NS_ERROR_UNEXPECTED
);
1461 JSContext
* cx
= jsapi
.cx();
1462 JS::Rooted
<JS::Value
> val(
1463 cx
, JS::ObjectValue(*GetJSObjForOSInfo(cx
, info
)));
1464 capturedPromise
->MaybeResolve(val
);
1466 [capturedPromise
](const nsresult rv
) {
1467 // Resolve with null when installYear is not available from the system
1468 capturedPromise
->MaybeResolve(JS::NullHandleValue
);
1471 promise
.forget(aResult
);
1477 nsSystemInfo::GetDiskInfo(JSContext
* aCx
, Promise
** aResult
) {
1478 NS_ENSURE_ARG_POINTER(aResult
);
1480 if (!XRE_IsParentProcess()) {
1481 return NS_ERROR_FAILURE
;
1484 nsIGlobalObject
* global
= xpc::CurrentNativeGlobal(aCx
);
1485 if (NS_WARN_IF(!global
)) {
1486 return NS_ERROR_FAILURE
;
1489 RefPtr
<Promise
> promise
= Promise::Create(global
, erv
);
1490 if (NS_WARN_IF(erv
.Failed())) {
1491 return erv
.StealNSResult();
1494 if (!mDiskInfoPromise
) {
1495 RefPtr
<nsISerialEventTarget
> backgroundET
= GetBackgroundTarget();
1496 nsCOMPtr
<nsIFile
> greDir
;
1497 nsCOMPtr
<nsIFile
> winDir
;
1498 nsCOMPtr
<nsIFile
> profDir
;
1499 nsresult rv
= NS_GetSpecialDirectory(NS_GRE_DIR
, getter_AddRefs(greDir
));
1500 if (NS_FAILED(rv
)) {
1503 rv
= NS_GetSpecialDirectory(NS_WIN_WINDOWS_DIR
, getter_AddRefs(winDir
));
1504 if (NS_FAILED(rv
)) {
1507 rv
= NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR
,
1508 getter_AddRefs(profDir
));
1509 if (NS_FAILED(rv
)) {
1514 InvokeAsync(backgroundET
, __func__
, [greDir
, winDir
, profDir
]() {
1516 nsresult rv
= CollectDiskInfo(greDir
, winDir
, profDir
, info
);
1517 if (NS_SUCCEEDED(rv
)) {
1518 return DiskInfoPromise::CreateAndResolve(info
, __func__
);
1520 return DiskInfoPromise::CreateAndReject(rv
, __func__
);
1524 // Chain the new promise to the extant mozpromise.
1525 RefPtr
<Promise
> capturedPromise
= promise
;
1526 mDiskInfoPromise
->Then(
1527 GetMainThreadSerialEventTarget(), __func__
,
1528 [capturedPromise
](const DiskInfo
& info
) {
1530 if (NS_WARN_IF(!jsapi
.Init(capturedPromise
->GetGlobalObject()))) {
1531 capturedPromise
->MaybeReject(NS_ERROR_UNEXPECTED
);
1534 JSContext
* cx
= jsapi
.cx();
1535 JS::Rooted
<JSObject
*> jsInfo(cx
, JS_NewPlainObject(cx
));
1536 // Store data in the rv:
1537 bool succeededSettingAllObjects
=
1538 jsInfo
&& GetJSObjForDiskInfo(cx
, jsInfo
, info
.binary
, "binary") &&
1539 GetJSObjForDiskInfo(cx
, jsInfo
, info
.profile
, "profile") &&
1540 GetJSObjForDiskInfo(cx
, jsInfo
, info
.system
, "system");
1541 // The above can fail due to OOM
1542 if (!succeededSettingAllObjects
) {
1543 JS_ClearPendingException(cx
);
1544 capturedPromise
->MaybeReject(NS_ERROR_FAILURE
);
1548 JS::Rooted
<JS::Value
> val(cx
, JS::ObjectValue(*jsInfo
));
1549 capturedPromise
->MaybeResolve(val
);
1551 [capturedPromise
](const nsresult rv
) {
1552 capturedPromise
->MaybeReject(rv
);
1555 promise
.forget(aResult
);
1560 NS_IMPL_ISUPPORTS_INHERITED(nsSystemInfo
, nsHashPropertyBag
, nsISystemInfo
)
1563 nsSystemInfo::GetCountryCode(JSContext
* aCx
, Promise
** aResult
) {
1564 NS_ENSURE_ARG_POINTER(aResult
);
1567 if (!XRE_IsParentProcess()) {
1568 return NS_ERROR_FAILURE
;
1570 #if defined(XP_MACOSX) || defined(XP_WIN)
1571 nsIGlobalObject
* global
= xpc::CurrentNativeGlobal(aCx
);
1572 if (NS_WARN_IF(!global
)) {
1573 return NS_ERROR_FAILURE
;
1577 RefPtr
<Promise
> promise
= Promise::Create(global
, erv
);
1578 if (NS_WARN_IF(erv
.Failed())) {
1579 return erv
.StealNSResult();
1582 if (!mCountryCodePromise
) {
1583 RefPtr
<nsISerialEventTarget
> backgroundET
= GetBackgroundTarget();
1585 mCountryCodePromise
= InvokeAsync(backgroundET
, __func__
, []() {
1586 nsAutoString countryCode
;
1588 nsresult rv
= GetSelectedCityInfo(countryCode
);
1591 nsresult rv
= CollectCountryCode(countryCode
);
1594 if (NS_SUCCEEDED(rv
)) {
1595 return CountryCodePromise::CreateAndResolve(countryCode
, __func__
);
1597 return CountryCodePromise::CreateAndReject(rv
, __func__
);
1601 RefPtr
<Promise
> capturedPromise
= promise
;
1602 mCountryCodePromise
->Then(
1603 GetMainThreadSerialEventTarget(), __func__
,
1604 [capturedPromise
](const nsString
& countryCode
) {
1606 if (NS_WARN_IF(!jsapi
.Init(capturedPromise
->GetGlobalObject()))) {
1607 capturedPromise
->MaybeReject(NS_ERROR_UNEXPECTED
);
1610 JSContext
* cx
= jsapi
.cx();
1611 JS::Rooted
<JSString
*> jsCountryCode(
1612 cx
, JS_NewUCStringCopyZ(cx
, countryCode
.get()));
1614 JS::Rooted
<JS::Value
> val(cx
, JS::StringValue(jsCountryCode
));
1615 capturedPromise
->MaybeResolve(val
);
1617 [capturedPromise
](const nsresult rv
) {
1618 // Resolve with null when countryCode is not available from the system
1619 capturedPromise
->MaybeResolve(JS::NullHandleValue
);
1622 promise
.forget(aResult
);
1628 nsSystemInfo::GetProcessInfo(JSContext
* aCx
, Promise
** aResult
) {
1629 NS_ENSURE_ARG_POINTER(aResult
);
1632 if (!XRE_IsParentProcess()) {
1633 return NS_ERROR_FAILURE
;
1636 nsIGlobalObject
* global
= xpc::CurrentNativeGlobal(aCx
);
1637 if (NS_WARN_IF(!global
)) {
1638 return NS_ERROR_FAILURE
;
1642 RefPtr
<Promise
> promise
= Promise::Create(global
, erv
);
1643 if (NS_WARN_IF(erv
.Failed())) {
1644 return erv
.StealNSResult();
1647 if (!mProcessInfoPromise
) {
1648 RefPtr
<nsISerialEventTarget
> backgroundET
= GetBackgroundTarget();
1650 mProcessInfoPromise
= InvokeAsync(backgroundET
, __func__
, []() {
1652 nsresult rv
= CollectProcessInfo(info
);
1653 if (NS_SUCCEEDED(rv
)) {
1654 return ProcessInfoPromise::CreateAndResolve(info
, __func__
);
1656 return ProcessInfoPromise::CreateAndReject(rv
, __func__
);
1660 // Chain the new promise to the extant mozpromise
1661 RefPtr
<Promise
> capturedPromise
= promise
;
1662 mProcessInfoPromise
->Then(
1663 GetMainThreadSerialEventTarget(), __func__
,
1664 [capturedPromise
](const ProcessInfo
& info
) {
1666 if (NS_WARN_IF(!jsapi
.Init(capturedPromise
->GetGlobalObject()))) {
1667 capturedPromise
->MaybeReject(NS_ERROR_UNEXPECTED
);
1670 JSContext
* cx
= jsapi
.cx();
1671 JS::Rooted
<JS::Value
> val(
1672 cx
, JS::ObjectValue(*GetJSObjForProcessInfo(cx
, info
)));
1673 capturedPromise
->MaybeResolve(val
);
1675 [capturedPromise
](const nsresult rv
) {
1676 // Resolve with null when installYear is not available from the system
1677 capturedPromise
->MaybeResolve(JS::NullHandleValue
);
1680 promise
.forget(aResult
);