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/Sprintf.h"
18 #include "mozilla/dom/Promise.h"
25 # endif // __MINGW32__
27 # include <winioctl.h>
30 # endif // __MINGW32__
31 # include "base/scoped_handle_win.h"
32 # include "mozilla/DynamicallyLinkedFunctionPtr.h"
33 # include "nsAppDirectoryServiceDefs.h"
34 # include "nsDirectoryServiceDefs.h"
35 # include "nsDirectoryServiceUtils.h"
36 # include "nsWindowsHelpers.h"
41 # include "MacHelpers.h"
49 #if defined(XP_LINUX) && !defined(ANDROID)
52 # include "mozilla/Tokenizer.h"
53 # include "nsCharSeparatedTokenizer.h"
59 #ifdef MOZ_WIDGET_ANDROID
60 # include "AndroidBuild.h"
61 # include "mozilla/java/GeckoAppShellWrappers.h"
62 # include "mozilla/jni/Utils.h"
66 # include <sys/sysctl.h>
69 #if defined(XP_LINUX) && defined(MOZ_SANDBOX)
70 # include "mozilla/SandboxInfo.h"
73 // Slot for NS_InitXPCOM to pass information to nsSystemInfo::Init.
74 // Only set to nonzero (potentially) if XP_UNIX. On such systems, the
75 // system call to discover the appropriate value is not thread-safe,
76 // so we must call it before going multithreaded, but nsSystemInfo::Init
77 // only happens well after that point.
78 uint32_t nsSystemInfo::gUserUmask
= 0;
80 using namespace mozilla::dom
;
82 #if defined(XP_LINUX) && !defined(ANDROID)
83 static void SimpleParseKeyValuePairs(
84 const std::string
& aFilename
,
85 std::map
<nsCString
, nsCString
>& aKeyValuePairs
) {
86 std::ifstream
input(aFilename
.c_str());
87 for (std::string line
; std::getline(input
, line
);) {
88 nsAutoCString key
, value
;
90 nsCCharSeparatedTokenizer
tokens(nsDependentCString(line
.c_str()), ':');
91 if (tokens
.hasMoreTokens()) {
92 key
= tokens
.nextToken();
93 if (tokens
.hasMoreTokens()) {
94 value
= tokens
.nextToken();
96 // We want the value even if there was just one token, to cover the
97 // case where we had the key, and the value was blank (seems to be
98 // a valid scenario some files.)
99 aKeyValuePairs
[key
] = value
;
106 // Lifted from media/webrtc/trunk/webrtc/base/systeminfo.cc,
107 // so keeping the _ instead of switching to camel case for now.
108 static void GetProcessorInformation(int* physical_cpus
, int* cache_size_L2
,
109 int* cache_size_L3
) {
110 MOZ_ASSERT(physical_cpus
&& cache_size_L2
&& cache_size_L3
);
113 *cache_size_L2
= 0; // This will be in kbytes
114 *cache_size_L3
= 0; // This will be in kbytes
116 // Determine buffer size, allocate and get processor information.
117 // Size can change between calls (unlikely), so a loop is done.
118 SYSTEM_LOGICAL_PROCESSOR_INFORMATION info_buffer
[32];
119 SYSTEM_LOGICAL_PROCESSOR_INFORMATION
* infos
= &info_buffer
[0];
120 DWORD return_length
= sizeof(info_buffer
);
121 while (!::GetLogicalProcessorInformation(infos
, &return_length
)) {
122 if (GetLastError() == ERROR_INSUFFICIENT_BUFFER
&&
123 infos
== &info_buffer
[0]) {
124 infos
= new SYSTEM_LOGICAL_PROCESSOR_INFORMATION
125 [return_length
/ sizeof(SYSTEM_LOGICAL_PROCESSOR_INFORMATION
)];
132 i
< return_length
/ sizeof(SYSTEM_LOGICAL_PROCESSOR_INFORMATION
); ++i
) {
133 if (infos
[i
].Relationship
== RelationProcessorCore
) {
135 } else if (infos
[i
].Relationship
== RelationCache
) {
136 // Only care about L2 and L3 cache
137 switch (infos
[i
].Cache
.Level
) {
139 *cache_size_L2
= static_cast<int>(infos
[i
].Cache
.Size
/ 1024);
142 *cache_size_L3
= static_cast<int>(infos
[i
].Cache
.Size
/ 1024);
149 if (infos
!= &info_buffer
[0]) {
158 static nsresult
GetFolderDiskInfo(nsIFile
* file
, FolderDiskInfo
& info
) {
159 info
.model
.Truncate();
160 info
.revision
.Truncate();
163 nsAutoString filePath
;
164 nsresult rv
= file
->GetPath(filePath
);
165 NS_ENSURE_SUCCESS(rv
, rv
);
166 wchar_t volumeMountPoint
[MAX_PATH
] = {L
'\\', L
'\\', L
'.', L
'\\'};
167 const size_t PREFIX_LEN
= 4;
168 if (!::GetVolumePathNameW(
169 filePath
.get(), volumeMountPoint
+ PREFIX_LEN
,
170 mozilla::ArrayLength(volumeMountPoint
) - PREFIX_LEN
)) {
171 return NS_ERROR_UNEXPECTED
;
173 size_t volumeMountPointLen
= wcslen(volumeMountPoint
);
174 // Since we would like to open a drive and not a directory, we need to
175 // remove any trailing backslash. A drive handle is valid for
176 // DeviceIoControl calls, a directory handle is not.
177 if (volumeMountPoint
[volumeMountPointLen
- 1] == L
'\\') {
178 volumeMountPoint
[volumeMountPointLen
- 1] = L
'\0';
180 ScopedHandle
handle(::CreateFileW(volumeMountPoint
, 0,
181 FILE_SHARE_READ
| FILE_SHARE_WRITE
, nullptr,
182 OPEN_EXISTING
, 0, nullptr));
183 if (!handle
.IsValid()) {
184 return NS_ERROR_UNEXPECTED
;
186 STORAGE_PROPERTY_QUERY queryParameters
= {StorageDeviceProperty
,
187 PropertyStandardQuery
};
188 STORAGE_DEVICE_DESCRIPTOR outputHeader
= {sizeof(STORAGE_DEVICE_DESCRIPTOR
)};
190 if (!::DeviceIoControl(handle
, IOCTL_STORAGE_QUERY_PROPERTY
, &queryParameters
,
191 sizeof(queryParameters
), &outputHeader
,
192 sizeof(outputHeader
), &bytesRead
, nullptr)) {
193 return NS_ERROR_FAILURE
;
195 PSTORAGE_DEVICE_DESCRIPTOR deviceOutput
=
196 (PSTORAGE_DEVICE_DESCRIPTOR
)malloc(outputHeader
.Size
);
197 if (!::DeviceIoControl(handle
, IOCTL_STORAGE_QUERY_PROPERTY
, &queryParameters
,
198 sizeof(queryParameters
), deviceOutput
,
199 outputHeader
.Size
, &bytesRead
, nullptr)) {
201 return NS_ERROR_FAILURE
;
204 queryParameters
.PropertyId
= StorageDeviceTrimProperty
;
207 DEVICE_TRIM_DESCRIPTOR trimDescriptor
= {sizeof(DEVICE_TRIM_DESCRIPTOR
)};
208 if (::DeviceIoControl(handle
, IOCTL_STORAGE_QUERY_PROPERTY
, &queryParameters
,
209 sizeof(queryParameters
), &trimDescriptor
,
210 sizeof(trimDescriptor
), &bytesRead
, nullptr)) {
211 if (trimDescriptor
.TrimEnabled
) {
218 queryParameters
.PropertyId
= StorageDeviceSeekPenaltyProperty
;
220 DEVICE_SEEK_PENALTY_DESCRIPTOR seekPenaltyDescriptor
= {
221 sizeof(DEVICE_SEEK_PENALTY_DESCRIPTOR
)};
222 if (::DeviceIoControl(handle
, IOCTL_STORAGE_QUERY_PROPERTY
,
223 &queryParameters
, sizeof(queryParameters
),
224 &seekPenaltyDescriptor
, sizeof(seekPenaltyDescriptor
),
225 &bytesRead
, nullptr)) {
226 // It is possible that the disk has TrimEnabled, but also
227 // IncursSeekPenalty; In this case, this is an HDD
228 if (seekPenaltyDescriptor
.IncursSeekPenalty
) {
234 // Some HDDs are including product ID info in the vendor field. Since PNP
235 // IDs include vendor info and product ID concatenated together, we'll do
236 // that here and interpret the result as a unique ID for the HDD model.
237 if (deviceOutput
->VendorIdOffset
) {
239 reinterpret_cast<char*>(deviceOutput
) + deviceOutput
->VendorIdOffset
;
241 if (deviceOutput
->ProductIdOffset
) {
243 reinterpret_cast<char*>(deviceOutput
) + deviceOutput
->ProductIdOffset
;
245 info
.model
.CompressWhitespace();
246 if (deviceOutput
->ProductRevisionOffset
) {
247 info
.revision
= reinterpret_cast<char*>(deviceOutput
) +
248 deviceOutput
->ProductRevisionOffset
;
249 info
.revision
.CompressWhitespace();
256 static nsresult
CollectDiskInfo(nsIFile
* greDir
, nsIFile
* winDir
,
257 nsIFile
* profDir
, DiskInfo
& info
) {
258 nsresult rv
= GetFolderDiskInfo(greDir
, info
.binary
);
262 rv
= GetFolderDiskInfo(winDir
, info
.system
);
266 return GetFolderDiskInfo(profDir
, info
.profile
);
269 static nsresult
CollectOSInfo(OSInfo
& info
) {
270 HKEY installYearHKey
;
271 LONG status
= RegOpenKeyExW(
272 HKEY_LOCAL_MACHINE
, L
"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion", 0,
273 KEY_READ
| KEY_WOW64_64KEY
, &installYearHKey
);
275 if (status
!= ERROR_SUCCESS
) {
276 return NS_ERROR_UNEXPECTED
;
279 nsAutoRegKey
installYearKey(installYearHKey
);
283 DWORD time_size
= sizeof(time_t);
285 status
= RegQueryValueExW(installYearHKey
, L
"InstallDate", nullptr, &type
,
286 (LPBYTE
)&raw_time
, &time_size
);
288 if (status
!= ERROR_SUCCESS
) {
289 return NS_ERROR_UNEXPECTED
;
292 if (type
!= REG_DWORD
) {
293 return NS_ERROR_UNEXPECTED
;
297 if (localtime_s(&time
, &raw_time
) != 0) {
298 return NS_ERROR_UNEXPECTED
;
301 info
.installYear
= 1900UL + time
.tm_year
;
303 nsAutoServiceHandle
scm(
304 OpenSCManager(nullptr, SERVICES_ACTIVE_DATABASE
, SC_MANAGER_CONNECT
));
307 return NS_ERROR_UNEXPECTED
;
310 bool superfetchServiceRunning
= false;
312 // Superfetch was introduced in Windows Vista as a service with the name
313 // SysMain. The service display name was also renamed to SysMain after Windows
315 nsAutoServiceHandle
hService(OpenService(scm
, L
"SysMain", GENERIC_READ
));
318 SERVICE_STATUS superfetchStatus
;
319 LPSERVICE_STATUS pSuperfetchStatus
= &superfetchStatus
;
321 if (!QueryServiceStatus(hService
, pSuperfetchStatus
)) {
322 return NS_ERROR_UNEXPECTED
;
325 superfetchServiceRunning
=
326 superfetchStatus
.dwCurrentState
== SERVICE_RUNNING
;
329 // If the SysMain (Superfetch) service is available, but not configured using
330 // the defaults, then it's disabled for our purposes, since it's not going to
331 // be operating as expected.
332 bool superfetchUsingDefaultParams
= true;
333 bool prefetchUsingDefaultParams
= true;
335 static const WCHAR prefetchParamsKeyName
[] =
336 L
"SYSTEM\\CurrentControlSet\\Control\\Session Manager\\Memory "
337 L
"Management\\PrefetchParameters";
338 static const DWORD SUPERFETCH_DEFAULT_PARAM
= 3;
339 static const DWORD PREFETCH_DEFAULT_PARAM
= 3;
341 HKEY prefetchParamsHKey
;
343 LONG prefetchParamsStatus
=
344 RegOpenKeyExW(HKEY_LOCAL_MACHINE
, prefetchParamsKeyName
, 0,
345 KEY_READ
| KEY_WOW64_64KEY
, &prefetchParamsHKey
);
347 if (prefetchParamsStatus
== ERROR_SUCCESS
) {
348 DWORD valueSize
= sizeof(DWORD
);
349 DWORD superfetchValue
= 0;
350 nsAutoRegKey
prefetchParamsKey(prefetchParamsHKey
);
351 LONG superfetchParamStatus
= RegQueryValueExW(
352 prefetchParamsHKey
, L
"EnableSuperfetch", nullptr, &type
,
353 reinterpret_cast<LPBYTE
>(&superfetchValue
), &valueSize
);
355 // If the EnableSuperfetch registry key doesn't exist, then it's using the
356 // default configuration.
357 if (superfetchParamStatus
== ERROR_SUCCESS
&&
358 superfetchValue
!= SUPERFETCH_DEFAULT_PARAM
) {
359 superfetchUsingDefaultParams
= false;
362 DWORD prefetchValue
= 0;
364 LONG prefetchParamStatus
= RegQueryValueExW(
365 prefetchParamsHKey
, L
"EnablePrefetcher", nullptr, &type
,
366 reinterpret_cast<LPBYTE
>(&prefetchValue
), &valueSize
);
368 // If the EnablePrefetcher registry key doesn't exist, then we interpret
369 // that as the Prefetcher being disabled (since Prefetch behaviour when
370 // the key is not available appears to be undefined).
371 if (prefetchParamStatus
!= ERROR_SUCCESS
||
372 prefetchValue
!= PREFETCH_DEFAULT_PARAM
) {
373 prefetchUsingDefaultParams
= false;
377 info
.hasSuperfetch
= superfetchServiceRunning
&& superfetchUsingDefaultParams
;
378 info
.hasPrefetch
= prefetchUsingDefaultParams
;
383 nsresult
CollectCountryCode(nsAString
& aCountryCode
) {
384 GEOID geoid
= GetUserGeoID(GEOCLASS_NATION
);
385 if (geoid
== GEOID_NOT_AVAILABLE
) {
386 return NS_ERROR_NOT_AVAILABLE
;
388 // Get required length
389 int numChars
= GetGeoInfoW(geoid
, GEO_ISO2
, nullptr, 0, 0);
391 return NS_ERROR_FAILURE
;
393 // Now get the string for real
394 aCountryCode
.SetLength(numChars
);
396 GetGeoInfoW(geoid
, GEO_ISO2
, char16ptr_t(aCountryCode
.BeginWriting()),
397 aCountryCode
.Length(), 0);
399 return NS_ERROR_FAILURE
;
402 // numChars includes null terminator
403 aCountryCode
.Truncate(numChars
- 1);
411 static HRESULT
EnumWSCProductList(nsAString
& aOutput
,
412 NotNull
<IWSCProductList
*> aProdList
) {
413 MOZ_ASSERT(aOutput
.IsEmpty());
416 HRESULT hr
= aProdList
->get_Count(&count
);
421 for (LONG index
= 0; index
< count
; ++index
) {
422 RefPtr
<IWscProduct
> product
;
423 hr
= aProdList
->get_Item(index
, getter_AddRefs(product
));
428 WSC_SECURITY_PRODUCT_STATE state
;
429 hr
= product
->get_ProductState(&state
);
434 // We only care about products that are active
435 if (state
== WSC_SECURITY_PRODUCT_STATE_OFF
||
436 state
== WSC_SECURITY_PRODUCT_STATE_SNOOZED
) {
441 hr
= product
->get_ProductName(bName
.GetAddress());
446 if (!aOutput
.IsEmpty()) {
447 aOutput
.AppendLiteral(u
";");
450 aOutput
.Append((wchar_t*)bName
, bName
.length());
456 static nsresult
GetWindowsSecurityCenterInfo(nsAString
& aAVInfo
,
457 nsAString
& aAntiSpyInfo
,
458 nsAString
& aFirewallInfo
) {
460 aAntiSpyInfo
.Truncate();
461 aFirewallInfo
.Truncate();
463 if (!XRE_IsParentProcess()) {
464 return NS_ERROR_NOT_AVAILABLE
;
467 const CLSID clsid
= __uuidof(WSCProductList
);
468 const IID iid
= __uuidof(IWSCProductList
);
470 // NB: A separate instance of IWSCProductList is needed for each distinct
471 // security provider type; MSDN says that we cannot reuse the same object
472 // and call Initialize() to pave over the previous data.
474 WSC_SECURITY_PROVIDER providerTypes
[] = {WSC_SECURITY_PROVIDER_ANTIVIRUS
,
475 WSC_SECURITY_PROVIDER_ANTISPYWARE
,
476 WSC_SECURITY_PROVIDER_FIREWALL
};
478 // Each output must match the corresponding entry in providerTypes.
479 nsAString
* outputs
[] = {&aAVInfo
, &aAntiSpyInfo
, &aFirewallInfo
};
481 static_assert(ArrayLength(providerTypes
) == ArrayLength(outputs
),
482 "Length of providerTypes and outputs arrays must match");
484 for (uint32_t index
= 0; index
< ArrayLength(providerTypes
); ++index
) {
485 RefPtr
<IWSCProductList
> prodList
;
486 HRESULT hr
= ::CoCreateInstance(clsid
, nullptr, CLSCTX_INPROC_SERVER
, iid
,
487 getter_AddRefs(prodList
));
489 return NS_ERROR_NOT_AVAILABLE
;
492 hr
= prodList
->Initialize(providerTypes
[index
]);
494 return NS_ERROR_UNEXPECTED
;
497 hr
= EnumWSCProductList(*outputs
[index
], WrapNotNull(prodList
.get()));
499 return NS_ERROR_UNEXPECTED
;
506 # endif // __MINGW32__
508 #endif // defined(XP_WIN)
511 static nsresult
GetAppleModelId(nsAutoCString
& aModelId
) {
513 size_t result
= sysctlbyname("hw.model", nullptr, &numChars
, nullptr, 0);
514 if (result
!= 0 || !numChars
) {
515 return NS_ERROR_FAILURE
;
517 aModelId
.SetLength(numChars
);
519 sysctlbyname("hw.model", aModelId
.BeginWriting(), &numChars
, nullptr, 0);
521 return NS_ERROR_FAILURE
;
523 // numChars includes null terminator
524 aModelId
.Truncate(numChars
- 1);
528 static nsresult
ProcessIsRosettaTranslated(bool& isRosetta
) {
529 # if defined(__aarch64__)
530 // There is no need to call sysctlbyname() if we are running as arm64.
534 size_t size
= sizeof(ret
);
535 if (sysctlbyname("sysctl.proc_translated", &ret
, &size
, NULL
, 0) == -1) {
536 if (errno
!= ENOENT
) {
537 fprintf(stderr
, "Failed to check for translation environment\n");
541 isRosetta
= (ret
== 1);
548 using namespace mozilla
;
550 nsSystemInfo::nsSystemInfo() = default;
552 nsSystemInfo::~nsSystemInfo() = default;
554 // CPU-specific information.
555 static const struct PropItems
{
557 bool (*propfun
)(void);
559 // x86-specific bits.
560 {"hasMMX", mozilla::supports_mmx
},
561 {"hasSSE", mozilla::supports_sse
},
562 {"hasSSE2", mozilla::supports_sse2
},
563 {"hasSSE3", mozilla::supports_sse3
},
564 {"hasSSSE3", mozilla::supports_ssse3
},
565 {"hasSSE4A", mozilla::supports_sse4a
},
566 {"hasSSE4_1", mozilla::supports_sse4_1
},
567 {"hasSSE4_2", mozilla::supports_sse4_2
},
568 {"hasAVX", mozilla::supports_avx
},
569 {"hasAVX2", mozilla::supports_avx2
},
570 {"hasAES", mozilla::supports_aes
},
571 // ARM-specific bits.
572 {"hasEDSP", mozilla::supports_edsp
},
573 {"hasARMv6", mozilla::supports_armv6
},
574 {"hasARMv7", mozilla::supports_armv7
},
575 {"hasNEON", mozilla::supports_neon
}};
577 nsresult
CollectProcessInfo(ProcessInfo
& info
) {
578 nsAutoCString cpuVendor
;
582 int cpuStepping
= -1;
583 int logicalCPUs
= -1;
584 int physicalCPUs
= -1;
585 int cacheSizeL2
= -1;
586 int cacheSizeL3
= -1;
589 // IsWow64Process2 is only available on Windows 10+, so we have to dynamically
590 // check for its existence.
591 typedef BOOL(WINAPI
* LPFN_IWP2
)(HANDLE
, USHORT
*, USHORT
*);
592 LPFN_IWP2 iwp2
= reinterpret_cast<LPFN_IWP2
>(
593 GetProcAddress(GetModuleHandle(L
"kernel32"), "IsWow64Process2"));
594 BOOL isWow64
= FALSE
;
595 USHORT processMachine
= IMAGE_FILE_MACHINE_UNKNOWN
;
596 USHORT nativeMachine
= IMAGE_FILE_MACHINE_UNKNOWN
;
599 gotWow64Value
= iwp2(GetCurrentProcess(), &processMachine
, &nativeMachine
);
601 isWow64
= (processMachine
!= IMAGE_FILE_MACHINE_UNKNOWN
);
604 gotWow64Value
= IsWow64Process(GetCurrentProcess(), &isWow64
);
605 // The function only indicates a WOW64 environment if it's 32-bit x86
606 // running on x86-64, so emulate what IsWow64Process2 would have given.
607 if (gotWow64Value
&& isWow64
) {
608 processMachine
= IMAGE_FILE_MACHINE_I386
;
609 nativeMachine
= IMAGE_FILE_MACHINE_AMD64
;
612 NS_WARNING_ASSERTION(gotWow64Value
, "IsWow64Process failed");
614 // Set this always, even for the x86-on-arm64 case.
615 info
.isWow64
= !!isWow64
;
616 // Additional information if we're running x86-on-arm64
617 info
.isWowARM64
= (processMachine
== IMAGE_FILE_MACHINE_I386
&&
618 nativeMachine
== IMAGE_FILE_MACHINE_ARM64
);
623 static const WCHAR keyName
[] =
624 L
"HARDWARE\\DESCRIPTION\\System\\CentralProcessor\\0";
626 if (RegOpenKeyEx(HKEY_LOCAL_MACHINE
, keyName
, 0, KEY_QUERY_VALUE
, &key
) ==
628 DWORD data
, len
, vtype
;
631 if (RegQueryValueEx(key
, L
"~Mhz", 0, 0, reinterpret_cast<LPBYTE
>(&data
),
632 &len
) == ERROR_SUCCESS
) {
633 cpuSpeed
= static_cast<int>(data
);
636 // Limit to 64 double byte characters, should be plenty, but create
637 // a buffer one larger as the result may not be null terminated. If
638 // it is more than 64, we will not get the value.
639 wchar_t cpuVendorStr
[64 + 1];
640 len
= sizeof(cpuVendorStr
) - 2;
641 if (RegQueryValueExW(key
, L
"VendorIdentifier", 0, &vtype
,
642 reinterpret_cast<LPBYTE
>(cpuVendorStr
),
643 &len
) == ERROR_SUCCESS
&&
644 vtype
== REG_SZ
&& len
% 2 == 0 && len
> 1) {
645 cpuVendorStr
[len
/ 2] = 0; // In case it isn't null terminated
646 CopyUTF16toUTF8(nsDependentString(cpuVendorStr
), cpuVendor
);
652 // Other CPU attributes:
654 GetNativeSystemInfo(&si
);
655 logicalCPUs
= si
.dwNumberOfProcessors
;
656 GetProcessorInformation(&physicalCPUs
, &cacheSizeL2
, &cacheSizeL3
);
657 if (physicalCPUs
<= 0) {
658 physicalCPUs
= logicalCPUs
;
660 cpuFamily
= si
.wProcessorLevel
;
661 cpuModel
= si
.wProcessorRevision
>> 8;
662 cpuStepping
= si
.wProcessorRevision
& 0xFF;
663 #elif defined(XP_MACOSX)
665 uint64_t sysctlValue64
= 0;
666 uint32_t sysctlValue32
= 0;
668 len
= sizeof(sysctlValue64
);
669 if (!sysctlbyname("hw.cpufrequency_max", &sysctlValue64
, &len
, NULL
, 0)) {
670 cpuSpeed
= static_cast<int>(sysctlValue64
/ 1000000);
672 MOZ_ASSERT(sizeof(sysctlValue64
) == len
);
674 len
= sizeof(sysctlValue32
);
675 if (!sysctlbyname("hw.physicalcpu_max", &sysctlValue32
, &len
, NULL
, 0)) {
676 physicalCPUs
= static_cast<int>(sysctlValue32
);
678 MOZ_ASSERT(sizeof(sysctlValue32
) == len
);
680 len
= sizeof(sysctlValue32
);
681 if (!sysctlbyname("hw.logicalcpu_max", &sysctlValue32
, &len
, NULL
, 0)) {
682 logicalCPUs
= static_cast<int>(sysctlValue32
);
684 MOZ_ASSERT(sizeof(sysctlValue32
) == len
);
686 len
= sizeof(sysctlValue64
);
687 if (!sysctlbyname("hw.l2cachesize", &sysctlValue64
, &len
, NULL
, 0)) {
688 cacheSizeL2
= static_cast<int>(sysctlValue64
/ 1024);
690 MOZ_ASSERT(sizeof(sysctlValue64
) == len
);
692 len
= sizeof(sysctlValue64
);
693 if (!sysctlbyname("hw.l3cachesize", &sysctlValue64
, &len
, NULL
, 0)) {
694 cacheSizeL3
= static_cast<int>(sysctlValue64
/ 1024);
696 MOZ_ASSERT(sizeof(sysctlValue64
) == len
);
698 if (!sysctlbyname("machdep.cpu.vendor", NULL
, &len
, NULL
, 0)) {
699 char* cpuVendorStr
= new char[len
];
700 if (!sysctlbyname("machdep.cpu.vendor", cpuVendorStr
, &len
, NULL
, 0)) {
701 cpuVendor
= cpuVendorStr
;
703 delete[] cpuVendorStr
;
706 len
= sizeof(sysctlValue32
);
707 if (!sysctlbyname("machdep.cpu.family", &sysctlValue32
, &len
, NULL
, 0)) {
708 cpuFamily
= static_cast<int>(sysctlValue32
);
710 MOZ_ASSERT(sizeof(sysctlValue32
) == len
);
712 len
= sizeof(sysctlValue32
);
713 if (!sysctlbyname("machdep.cpu.model", &sysctlValue32
, &len
, NULL
, 0)) {
714 cpuModel
= static_cast<int>(sysctlValue32
);
716 MOZ_ASSERT(sizeof(sysctlValue32
) == len
);
718 len
= sizeof(sysctlValue32
);
719 if (!sysctlbyname("machdep.cpu.stepping", &sysctlValue32
, &len
, NULL
, 0)) {
720 cpuStepping
= static_cast<int>(sysctlValue32
);
722 MOZ_ASSERT(sizeof(sysctlValue32
) == len
);
724 #elif defined(XP_LINUX) && !defined(ANDROID)
725 // Get vendor, family, model, stepping, physical cores
726 // from /proc/cpuinfo file
728 std::map
<nsCString
, nsCString
> keyValuePairs
;
729 SimpleParseKeyValuePairs("/proc/cpuinfo", keyValuePairs
);
731 // cpuVendor from "vendor_id"
732 info
.cpuVendor
.Assign(keyValuePairs
["vendor_id"_ns
]);
735 // cpuFamily from "cpu family"
737 Tokenizer
p(keyValuePairs
["cpu family"_ns
]);
738 if (p
.Next(t
) && t
.Type() == Tokenizer::TOKEN_INTEGER
&&
739 t
.AsInteger() <= INT32_MAX
) {
740 cpuFamily
= static_cast<int>(t
.AsInteger());
745 // cpuModel from "model"
747 Tokenizer
p(keyValuePairs
["model"_ns
]);
748 if (p
.Next(t
) && t
.Type() == Tokenizer::TOKEN_INTEGER
&&
749 t
.AsInteger() <= INT32_MAX
) {
750 cpuModel
= static_cast<int>(t
.AsInteger());
755 // cpuStepping from "stepping"
757 Tokenizer
p(keyValuePairs
["stepping"_ns
]);
758 if (p
.Next(t
) && t
.Type() == Tokenizer::TOKEN_INTEGER
&&
759 t
.AsInteger() <= INT32_MAX
) {
760 cpuStepping
= static_cast<int>(t
.AsInteger());
765 // physicalCPUs from "cpu cores"
767 Tokenizer
p(keyValuePairs
["cpu cores"_ns
]);
768 if (p
.Next(t
) && t
.Type() == Tokenizer::TOKEN_INTEGER
&&
769 t
.AsInteger() <= INT32_MAX
) {
770 physicalCPUs
= static_cast<int>(t
.AsInteger());
776 // Get cpuSpeed from another file.
778 "/sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_max_freq");
780 if (getline(input
, line
)) {
782 Tokenizer
p(line
.c_str());
783 if (p
.Next(t
) && t
.Type() == Tokenizer::TOKEN_INTEGER
&&
784 t
.AsInteger() <= INT32_MAX
) {
785 cpuSpeed
= static_cast<int>(t
.AsInteger() / 1000);
791 // Get cacheSizeL2 from yet another file
792 std::ifstream
input("/sys/devices/system/cpu/cpu0/cache/index2/size");
794 if (getline(input
, line
)) {
796 Tokenizer
p(line
.c_str(), nullptr, "K");
797 if (p
.Next(t
) && t
.Type() == Tokenizer::TOKEN_INTEGER
&&
798 t
.AsInteger() <= INT32_MAX
) {
799 cacheSizeL2
= static_cast<int>(t
.AsInteger());
805 // Get cacheSizeL3 from yet another file
806 std::ifstream
input("/sys/devices/system/cpu/cpu0/cache/index3/size");
808 if (getline(input
, line
)) {
810 Tokenizer
p(line
.c_str(), nullptr, "K");
811 if (p
.Next(t
) && t
.Type() == Tokenizer::TOKEN_INTEGER
&&
812 t
.AsInteger() <= INT32_MAX
) {
813 cacheSizeL3
= static_cast<int>(t
.AsInteger());
818 info
.cpuCount
= PR_GetNumberOfProcessors();
820 info
.cpuCount
= PR_GetNumberOfProcessors();
824 info
.cpuSpeed
= cpuSpeed
;
828 if (!cpuVendor
.IsEmpty()) {
829 info
.cpuVendor
= cpuVendor
;
831 if (cpuFamily
>= 0) {
832 info
.cpuFamily
= cpuFamily
;
835 info
.cpuModel
= cpuModel
;
837 if (cpuStepping
>= 0) {
838 info
.cpuStepping
= cpuStepping
;
841 if (logicalCPUs
>= 0) {
842 info
.cpuCount
= logicalCPUs
;
844 if (physicalCPUs
>= 0) {
845 info
.cpuCores
= physicalCPUs
;
848 if (cacheSizeL2
>= 0) {
849 info
.l2cacheKB
= cacheSizeL2
;
851 if (cacheSizeL3
>= 0) {
852 info
.l3cacheKB
= cacheSizeL3
;
858 #if defined(XP_WIN) && (_WIN32_WINNT < 0x0A00)
860 BOOL WINAPI
IsUserCetAvailableInEnvironment(_In_ DWORD UserCetEnvironment
);
862 # define USER_CET_ENVIRONMENT_WIN32_PROCESS 0x00000000
865 nsresult
nsSystemInfo::Init() {
866 // check that it is called from the main thread on all platforms.
867 MOZ_ASSERT(NS_IsMainThread());
871 static const struct {
874 } items
[] = {{PR_SI_SYSNAME
, "name"},
875 {PR_SI_ARCHITECTURE
, "arch"},
876 {PR_SI_RELEASE
, "version"},
877 {PR_SI_RELEASE_BUILD
, "build"}};
879 for (uint32_t i
= 0; i
< (sizeof(items
) / sizeof(items
[0])); i
++) {
880 char buf
[SYS_INFO_BUFFER_LENGTH
];
881 if (PR_GetSystemInfo(items
[i
].cmd
, buf
, sizeof(buf
)) == PR_SUCCESS
) {
882 rv
= SetPropertyAsACString(NS_ConvertASCIItoUTF16(items
[i
].name
),
883 nsDependentCString(buf
));
884 if (NS_WARN_IF(NS_FAILED(rv
))) {
888 NS_WARNING("PR_GetSystemInfo failed");
892 rv
= SetPropertyAsBool(NS_ConvertASCIItoUTF16("hasWindowsTouchInterface"),
894 NS_ENSURE_SUCCESS(rv
, rv
);
896 // Additional informations not available through PR_GetSystemInfo.
897 SetInt32Property(u
"pagesize"_ns
, PR_GetPageSize());
898 SetInt32Property(u
"pageshift"_ns
, PR_GetPageShift());
899 SetInt32Property(u
"memmapalign"_ns
, PR_GetMemMapAlignment());
900 SetUint64Property(u
"memsize"_ns
, PR_GetPhysicalMemorySize());
901 SetUint32Property(u
"umask"_ns
, nsSystemInfo::gUserUmask
);
903 uint64_t virtualMem
= 0;
907 MEMORYSTATUSEX memStat
;
908 memStat
.dwLength
= sizeof(memStat
);
909 if (GlobalMemoryStatusEx(&memStat
)) {
910 virtualMem
= memStat
.ullTotalVirtual
;
913 if (virtualMem
) SetUint64Property(u
"virtualmemsize"_ns
, virtualMem
);
915 for (uint32_t i
= 0; i
< ArrayLength(cpuPropItems
); i
++) {
916 rv
= SetPropertyAsBool(NS_ConvertASCIItoUTF16(cpuPropItems
[i
].name
),
917 cpuPropItems
[i
].propfun());
918 if (NS_WARN_IF(NS_FAILED(rv
))) {
930 rv
= SetPropertyAsBool(u
"isMinGW"_ns
, !!isMinGW
);
931 if (NS_WARN_IF(NS_FAILED(rv
))) {
936 nsAutoString avInfo
, antiSpyInfo
, firewallInfo
;
938 GetWindowsSecurityCenterInfo(avInfo
, antiSpyInfo
, firewallInfo
))) {
939 if (!avInfo
.IsEmpty()) {
940 rv
= SetPropertyAsAString(u
"registeredAntiVirus"_ns
, avInfo
);
941 if (NS_WARN_IF(NS_FAILED(rv
))) {
946 if (!antiSpyInfo
.IsEmpty()) {
947 rv
= SetPropertyAsAString(u
"registeredAntiSpyware"_ns
, antiSpyInfo
);
948 if (NS_WARN_IF(NS_FAILED(rv
))) {
953 if (!firewallInfo
.IsEmpty()) {
954 rv
= SetPropertyAsAString(u
"registeredFirewall"_ns
, firewallInfo
);
955 if (NS_WARN_IF(NS_FAILED(rv
))) {
960 # endif // __MINGW32__
962 mozilla::DynamicallyLinkedFunctionPtr
<decltype(
963 &IsUserCetAvailableInEnvironment
)>
964 isUserCetAvailable(L
"api-ms-win-core-sysinfo-l1-2-6.dll",
965 "IsUserCetAvailableInEnvironment");
966 bool hasUserCET
= isUserCetAvailable
&&
967 isUserCetAvailable(USER_CET_ENVIRONMENT_WIN32_PROCESS
);
968 rv
= SetPropertyAsBool(u
"hasUserCET"_ns
, hasUserCET
);
969 if (NS_WARN_IF(NS_FAILED(rv
))) {
975 #if defined(XP_MACOSX)
976 nsAutoCString modelId
;
977 if (NS_SUCCEEDED(GetAppleModelId(modelId
))) {
978 rv
= SetPropertyAsACString(u
"appleModelId"_ns
, modelId
);
979 NS_ENSURE_SUCCESS(rv
, rv
);
982 if (NS_SUCCEEDED(ProcessIsRosettaTranslated(isRosetta
))) {
983 rv
= SetPropertyAsBool(u
"rosettaStatus"_ns
, isRosetta
);
984 NS_ENSURE_SUCCESS(rv
, rv
);
988 #if defined(MOZ_WIDGET_GTK)
989 // This must be done here because NSPR can only separate OS's when compiled,
990 // not libraries. 64 bytes is going to be well enough for "GTK " followed by 3
991 // integers separated with dots.
993 ssize_t gtkver_len
= 0;
995 if (gtkver_len
<= 0) {
996 gtkver_len
= SprintfLiteral(gtkver
, "GTK %u.%u.%u", gtk_major_version
,
997 gtk_minor_version
, gtk_micro_version
);
1000 nsAutoCString secondaryLibrary
;
1001 if (gtkver_len
> 0 && gtkver_len
< int(sizeof(gtkver
))) {
1002 secondaryLibrary
.Append(nsDependentCSubstring(gtkver
, gtkver_len
));
1006 // With TSan, avoid loading libpulse here because we cannot unload it
1007 // afterwards due to restrictions from TSan about unloading libraries
1008 // matched by the suppression list.
1009 void* libpulse
= dlopen("libpulse.so.0", RTLD_LAZY
);
1010 const char* libpulseVersion
= "not-available";
1012 auto pa_get_library_version
= reinterpret_cast<const char* (*)()>(
1013 dlsym(libpulse
, "pa_get_library_version"));
1015 if (pa_get_library_version
) {
1016 libpulseVersion
= pa_get_library_version();
1020 secondaryLibrary
.AppendPrintf(",libpulse %s", libpulseVersion
);
1027 rv
= SetPropertyAsACString(u
"secondaryLibrary"_ns
, secondaryLibrary
);
1028 if (NS_WARN_IF(NS_FAILED(rv
))) {
1033 #ifdef MOZ_WIDGET_ANDROID
1034 AndroidSystemInfo info
;
1035 GetAndroidSystemInfo(&info
);
1036 SetupAndroidInfo(info
);
1039 #if defined(XP_LINUX) && defined(MOZ_SANDBOX)
1040 SandboxInfo sandInfo
= SandboxInfo::Get();
1042 SetPropertyAsBool(u
"hasSeccompBPF"_ns
,
1043 sandInfo
.Test(SandboxInfo::kHasSeccompBPF
));
1044 SetPropertyAsBool(u
"hasSeccompTSync"_ns
,
1045 sandInfo
.Test(SandboxInfo::kHasSeccompTSync
));
1046 SetPropertyAsBool(u
"hasUserNamespaces"_ns
,
1047 sandInfo
.Test(SandboxInfo::kHasUserNamespaces
));
1048 SetPropertyAsBool(u
"hasPrivilegedUserNamespaces"_ns
,
1049 sandInfo
.Test(SandboxInfo::kHasPrivilegedUserNamespaces
));
1051 if (sandInfo
.Test(SandboxInfo::kEnabledForContent
)) {
1052 SetPropertyAsBool(u
"canSandboxContent"_ns
, sandInfo
.CanSandboxContent());
1055 if (sandInfo
.Test(SandboxInfo::kEnabledForMedia
)) {
1056 SetPropertyAsBool(u
"canSandboxMedia"_ns
, sandInfo
.CanSandboxMedia());
1058 #endif // XP_LINUX && MOZ_SANDBOX
1063 #ifdef MOZ_WIDGET_ANDROID
1064 // Prerelease versions of Android use a letter instead of version numbers.
1065 // Unfortunately this breaks websites due to the user agent.
1066 // Chrome works around this by hardcoding an Android version when a
1067 // numeric version can't be obtained. We're doing the same.
1068 // This version will need to be updated whenever there is a new official
1069 // Android release. Search for "kDefaultAndroidMajorVersion" in:
1070 // https://source.chromium.org/chromium/chromium/src/+/master:base/system/sys_info_android.cc
1071 # define DEFAULT_ANDROID_VERSION u"10.0.99"
1074 void nsSystemInfo::GetAndroidSystemInfo(AndroidSystemInfo
* aInfo
) {
1075 if (!jni::IsAvailable()) {
1076 // called from xpcshell etc.
1077 aInfo
->sdk_version() = 0;
1081 jni::String::LocalRef model
= java::sdk::Build::MODEL();
1082 aInfo
->device() = model
->ToString();
1084 jni::String::LocalRef manufacturer
=
1085 mozilla::java::sdk::Build::MANUFACTURER();
1086 aInfo
->manufacturer() = manufacturer
->ToString();
1088 jni::String::LocalRef hardware
= java::sdk::Build::HARDWARE();
1089 aInfo
->hardware() = hardware
->ToString();
1091 jni::String::LocalRef release
= java::sdk::VERSION::RELEASE();
1092 nsString
str(release
->ToString());
1096 int num_read
= sscanf(NS_ConvertUTF16toUTF8(str
).get(), "%d.%d.%d",
1097 &major_version
, &minor_version
, &bugfix_version
);
1098 if (num_read
== 0) {
1099 aInfo
->release_version() = nsLiteralString(DEFAULT_ANDROID_VERSION
);
1101 aInfo
->release_version() = str
;
1104 aInfo
->sdk_version() = jni::GetAPIVersion();
1105 aInfo
->isTablet() = java::GeckoAppShell::IsTablet();
1108 void nsSystemInfo::SetupAndroidInfo(const AndroidSystemInfo
& aInfo
) {
1109 if (!aInfo
.device().IsEmpty()) {
1110 SetPropertyAsAString(u
"device"_ns
, aInfo
.device());
1112 if (!aInfo
.manufacturer().IsEmpty()) {
1113 SetPropertyAsAString(u
"manufacturer"_ns
, aInfo
.manufacturer());
1115 if (!aInfo
.release_version().IsEmpty()) {
1116 SetPropertyAsAString(u
"release_version"_ns
, aInfo
.release_version());
1118 SetPropertyAsBool(u
"tablet"_ns
, aInfo
.isTablet());
1119 // NSPR "version" is the kernel version. For Android we want the Android
1120 // version. Rename SDK version to version and put the kernel version into
1123 nsresult rv
= GetPropertyAsAString(u
"version"_ns
, str
);
1124 if (NS_SUCCEEDED(rv
)) {
1125 SetPropertyAsAString(u
"kernel_version"_ns
, str
);
1127 // When JNI is not available (eg. in xpcshell tests), sdk_version is 0.
1128 if (aInfo
.sdk_version() != 0) {
1129 if (!aInfo
.hardware().IsEmpty()) {
1130 SetPropertyAsAString(u
"hardware"_ns
, aInfo
.hardware());
1132 SetPropertyAsInt32(u
"version"_ns
, aInfo
.sdk_version());
1135 #endif // MOZ_WIDGET_ANDROID
1137 void nsSystemInfo::SetInt32Property(const nsAString
& aPropertyName
,
1138 const int32_t aValue
) {
1139 NS_WARNING_ASSERTION(aValue
> 0, "Unable to read system value");
1144 SetPropertyAsInt32(aPropertyName
, aValue
);
1145 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv
), "Unable to set property");
1149 void nsSystemInfo::SetUint32Property(const nsAString
& aPropertyName
,
1150 const uint32_t aValue
) {
1151 // Only one property is currently set via this function.
1152 // It may legitimately be zero.
1156 SetPropertyAsUint32(aPropertyName
, aValue
);
1157 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv
), "Unable to set property");
1160 void nsSystemInfo::SetUint64Property(const nsAString
& aPropertyName
,
1161 const uint64_t aValue
) {
1162 NS_WARNING_ASSERTION(aValue
> 0, "Unable to read system value");
1167 SetPropertyAsUint64(aPropertyName
, aValue
);
1168 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv
), "Unable to set property");
1174 static bool GetJSObjForDiskInfo(JSContext
* aCx
, JS::Handle
<JSObject
*> aParent
,
1175 const FolderDiskInfo
& info
,
1176 const char* propName
) {
1177 JS::Rooted
<JSObject
*> jsInfo(aCx
, JS_NewPlainObject(aCx
));
1182 JSString
* strModel
=
1183 JS_NewStringCopyN(aCx
, info
.model
.get(), info
.model
.Length());
1187 JS::Rooted
<JS::Value
> valModel(aCx
, JS::StringValue(strModel
));
1188 if (!JS_SetProperty(aCx
, jsInfo
, "model", valModel
)) {
1192 JSString
* strRevision
=
1193 JS_NewStringCopyN(aCx
, info
.revision
.get(), info
.revision
.Length());
1197 JS::Rooted
<JS::Value
> valRevision(aCx
, JS::StringValue(strRevision
));
1198 if (!JS_SetProperty(aCx
, jsInfo
, "revision", valRevision
)) {
1202 JSString
* strSSD
= JS_NewStringCopyZ(aCx
, info
.isSSD
? "SSD" : "HDD");
1206 JS::Rooted
<JS::Value
> valSSD(aCx
, JS::StringValue(strSSD
));
1207 if (!JS_SetProperty(aCx
, jsInfo
, "type", valSSD
)) {
1211 JS::Rooted
<JS::Value
> val(aCx
, JS::ObjectValue(*jsInfo
));
1212 return JS_SetProperty(aCx
, aParent
, propName
, val
);
1215 JSObject
* GetJSObjForOSInfo(JSContext
* aCx
, const OSInfo
& info
) {
1216 JS::Rooted
<JSObject
*> jsInfo(aCx
, JS_NewPlainObject(aCx
));
1218 JS::Rooted
<JS::Value
> valInstallYear(aCx
, JS::Int32Value(info
.installYear
));
1219 JS_SetProperty(aCx
, jsInfo
, "installYear", valInstallYear
);
1221 JS::Rooted
<JS::Value
> valHasSuperfetch(aCx
,
1222 JS::BooleanValue(info
.hasSuperfetch
));
1223 JS_SetProperty(aCx
, jsInfo
, "hasSuperfetch", valHasSuperfetch
);
1225 JS::Rooted
<JS::Value
> valHasPrefetch(aCx
, JS::BooleanValue(info
.hasPrefetch
));
1226 JS_SetProperty(aCx
, jsInfo
, "hasPrefetch", valHasPrefetch
);
1233 JSObject
* GetJSObjForProcessInfo(JSContext
* aCx
, const ProcessInfo
& info
) {
1234 JS::Rooted
<JSObject
*> jsInfo(aCx
, JS_NewPlainObject(aCx
));
1237 JS::Rooted
<JS::Value
> valisWow64(aCx
, JS::BooleanValue(info
.isWow64
));
1238 JS_SetProperty(aCx
, jsInfo
, "isWow64", valisWow64
);
1240 JS::Rooted
<JS::Value
> valisWowARM64(aCx
, JS::BooleanValue(info
.isWowARM64
));
1241 JS_SetProperty(aCx
, jsInfo
, "isWowARM64", valisWowARM64
);
1244 JS::Rooted
<JS::Value
> valCountInfo(aCx
, JS::Int32Value(info
.cpuCount
));
1245 JS_SetProperty(aCx
, jsInfo
, "count", valCountInfo
);
1247 JS::Rooted
<JS::Value
> valCoreInfo(aCx
, JS::Int32Value(info
.cpuCores
));
1248 JS_SetProperty(aCx
, jsInfo
, "cores", valCoreInfo
);
1250 JSString
* strVendor
=
1251 JS_NewStringCopyN(aCx
, info
.cpuVendor
.get(), info
.cpuVendor
.Length());
1252 JS::Rooted
<JS::Value
> valVendor(aCx
, JS::StringValue(strVendor
));
1253 JS_SetProperty(aCx
, jsInfo
, "vendor", valVendor
);
1255 JS::Rooted
<JS::Value
> valFamilyInfo(aCx
, JS::Int32Value(info
.cpuFamily
));
1256 JS_SetProperty(aCx
, jsInfo
, "family", valFamilyInfo
);
1258 JS::Rooted
<JS::Value
> valModelInfo(aCx
, JS::Int32Value(info
.cpuModel
));
1259 JS_SetProperty(aCx
, jsInfo
, "model", valModelInfo
);
1261 JS::Rooted
<JS::Value
> valSteppingInfo(aCx
, JS::Int32Value(info
.cpuStepping
));
1262 JS_SetProperty(aCx
, jsInfo
, "stepping", valSteppingInfo
);
1264 JS::Rooted
<JS::Value
> valL2CacheInfo(aCx
, JS::Int32Value(info
.l2cacheKB
));
1265 JS_SetProperty(aCx
, jsInfo
, "l2cacheKB", valL2CacheInfo
);
1267 JS::Rooted
<JS::Value
> valL3CacheInfo(aCx
, JS::Int32Value(info
.l3cacheKB
));
1268 JS_SetProperty(aCx
, jsInfo
, "l3cacheKB", valL3CacheInfo
);
1270 JS::Rooted
<JS::Value
> valSpeedInfo(aCx
, JS::Int32Value(info
.cpuSpeed
));
1271 JS_SetProperty(aCx
, jsInfo
, "speedMHz", valSpeedInfo
);
1276 RefPtr
<nsISerialEventTarget
> nsSystemInfo::GetBackgroundTarget() {
1277 if (!mBackgroundET
) {
1278 MOZ_ALWAYS_SUCCEEDS(NS_CreateBackgroundTaskQueue(
1279 "SystemInfoThread", getter_AddRefs(mBackgroundET
)));
1281 return mBackgroundET
;
1285 nsSystemInfo::GetOsInfo(JSContext
* aCx
, Promise
** aResult
) {
1286 NS_ENSURE_ARG_POINTER(aResult
);
1288 if (!XRE_IsParentProcess()) {
1289 return NS_ERROR_FAILURE
;
1292 nsIGlobalObject
* global
= xpc::CurrentNativeGlobal(aCx
);
1293 if (NS_WARN_IF(!global
)) {
1294 return NS_ERROR_FAILURE
;
1298 RefPtr
<Promise
> promise
= Promise::Create(global
, erv
);
1299 if (NS_WARN_IF(erv
.Failed())) {
1300 return erv
.StealNSResult();
1303 if (!mOSInfoPromise
) {
1304 RefPtr
<nsISerialEventTarget
> backgroundET
= GetBackgroundTarget();
1306 mOSInfoPromise
= InvokeAsync(backgroundET
, __func__
, []() {
1308 nsresult rv
= CollectOSInfo(info
);
1309 if (NS_SUCCEEDED(rv
)) {
1310 return OSInfoPromise::CreateAndResolve(info
, __func__
);
1312 return OSInfoPromise::CreateAndReject(rv
, __func__
);
1316 // Chain the new promise to the extant mozpromise
1317 RefPtr
<Promise
> capturedPromise
= promise
;
1318 mOSInfoPromise
->Then(
1319 GetMainThreadSerialEventTarget(), __func__
,
1320 [capturedPromise
](const OSInfo
& info
) {
1322 if (NS_WARN_IF(!jsapi
.Init(capturedPromise
->GetGlobalObject()))) {
1323 capturedPromise
->MaybeReject(NS_ERROR_UNEXPECTED
);
1326 JSContext
* cx
= jsapi
.cx();
1327 JS::Rooted
<JS::Value
> val(
1328 cx
, JS::ObjectValue(*GetJSObjForOSInfo(cx
, info
)));
1329 capturedPromise
->MaybeResolve(val
);
1331 [capturedPromise
](const nsresult rv
) {
1332 // Resolve with null when installYear is not available from the system
1333 capturedPromise
->MaybeResolve(JS::NullHandleValue
);
1336 promise
.forget(aResult
);
1342 nsSystemInfo::GetDiskInfo(JSContext
* aCx
, Promise
** aResult
) {
1343 NS_ENSURE_ARG_POINTER(aResult
);
1345 if (!XRE_IsParentProcess()) {
1346 return NS_ERROR_FAILURE
;
1349 nsIGlobalObject
* global
= xpc::CurrentNativeGlobal(aCx
);
1350 if (NS_WARN_IF(!global
)) {
1351 return NS_ERROR_FAILURE
;
1354 RefPtr
<Promise
> promise
= Promise::Create(global
, erv
);
1355 if (NS_WARN_IF(erv
.Failed())) {
1356 return erv
.StealNSResult();
1359 if (!mDiskInfoPromise
) {
1360 RefPtr
<nsISerialEventTarget
> backgroundET
= GetBackgroundTarget();
1361 nsCOMPtr
<nsIFile
> greDir
;
1362 nsCOMPtr
<nsIFile
> winDir
;
1363 nsCOMPtr
<nsIFile
> profDir
;
1364 nsresult rv
= NS_GetSpecialDirectory(NS_GRE_DIR
, getter_AddRefs(greDir
));
1365 if (NS_FAILED(rv
)) {
1368 rv
= NS_GetSpecialDirectory(NS_WIN_WINDOWS_DIR
, getter_AddRefs(winDir
));
1369 if (NS_FAILED(rv
)) {
1372 rv
= NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR
,
1373 getter_AddRefs(profDir
));
1374 if (NS_FAILED(rv
)) {
1379 InvokeAsync(backgroundET
, __func__
, [greDir
, winDir
, profDir
]() {
1381 nsresult rv
= CollectDiskInfo(greDir
, winDir
, profDir
, info
);
1382 if (NS_SUCCEEDED(rv
)) {
1383 return DiskInfoPromise::CreateAndResolve(info
, __func__
);
1385 return DiskInfoPromise::CreateAndReject(rv
, __func__
);
1389 // Chain the new promise to the extant mozpromise.
1390 RefPtr
<Promise
> capturedPromise
= promise
;
1391 mDiskInfoPromise
->Then(
1392 GetMainThreadSerialEventTarget(), __func__
,
1393 [capturedPromise
](const DiskInfo
& info
) {
1395 if (NS_WARN_IF(!jsapi
.Init(capturedPromise
->GetGlobalObject()))) {
1396 capturedPromise
->MaybeReject(NS_ERROR_UNEXPECTED
);
1399 JSContext
* cx
= jsapi
.cx();
1400 JS::Rooted
<JSObject
*> jsInfo(cx
, JS_NewPlainObject(cx
));
1401 // Store data in the rv:
1402 bool succeededSettingAllObjects
=
1403 jsInfo
&& GetJSObjForDiskInfo(cx
, jsInfo
, info
.binary
, "binary") &&
1404 GetJSObjForDiskInfo(cx
, jsInfo
, info
.profile
, "profile") &&
1405 GetJSObjForDiskInfo(cx
, jsInfo
, info
.system
, "system");
1406 // The above can fail due to OOM
1407 if (!succeededSettingAllObjects
) {
1408 JS_ClearPendingException(cx
);
1409 capturedPromise
->MaybeReject(NS_ERROR_FAILURE
);
1413 JS::Rooted
<JS::Value
> val(cx
, JS::ObjectValue(*jsInfo
));
1414 capturedPromise
->MaybeResolve(val
);
1416 [capturedPromise
](const nsresult rv
) {
1417 capturedPromise
->MaybeReject(rv
);
1420 promise
.forget(aResult
);
1425 NS_IMPL_ISUPPORTS_INHERITED(nsSystemInfo
, nsHashPropertyBag
, nsISystemInfo
)
1428 nsSystemInfo::GetCountryCode(JSContext
* aCx
, Promise
** aResult
) {
1429 NS_ENSURE_ARG_POINTER(aResult
);
1432 if (!XRE_IsParentProcess()) {
1433 return NS_ERROR_FAILURE
;
1435 #if defined(XP_MACOSX) || defined(XP_WIN)
1436 nsIGlobalObject
* global
= xpc::CurrentNativeGlobal(aCx
);
1437 if (NS_WARN_IF(!global
)) {
1438 return NS_ERROR_FAILURE
;
1442 RefPtr
<Promise
> promise
= Promise::Create(global
, erv
);
1443 if (NS_WARN_IF(erv
.Failed())) {
1444 return erv
.StealNSResult();
1447 if (!mCountryCodePromise
) {
1448 RefPtr
<nsISerialEventTarget
> backgroundET
= GetBackgroundTarget();
1450 mCountryCodePromise
= InvokeAsync(backgroundET
, __func__
, []() {
1451 nsAutoString countryCode
;
1453 nsresult rv
= GetSelectedCityInfo(countryCode
);
1456 nsresult rv
= CollectCountryCode(countryCode
);
1459 if (NS_SUCCEEDED(rv
)) {
1460 return CountryCodePromise::CreateAndResolve(countryCode
, __func__
);
1462 return CountryCodePromise::CreateAndReject(rv
, __func__
);
1466 RefPtr
<Promise
> capturedPromise
= promise
;
1467 mCountryCodePromise
->Then(
1468 GetMainThreadSerialEventTarget(), __func__
,
1469 [capturedPromise
](const nsString
& countryCode
) {
1471 if (NS_WARN_IF(!jsapi
.Init(capturedPromise
->GetGlobalObject()))) {
1472 capturedPromise
->MaybeReject(NS_ERROR_UNEXPECTED
);
1475 JSContext
* cx
= jsapi
.cx();
1476 JS::Rooted
<JSString
*> jsCountryCode(
1477 cx
, JS_NewUCStringCopyZ(cx
, countryCode
.get()));
1479 JS::Rooted
<JS::Value
> val(cx
, JS::StringValue(jsCountryCode
));
1480 capturedPromise
->MaybeResolve(val
);
1482 [capturedPromise
](const nsresult rv
) {
1483 // Resolve with null when countryCode is not available from the system
1484 capturedPromise
->MaybeResolve(JS::NullHandleValue
);
1487 promise
.forget(aResult
);
1493 nsSystemInfo::GetProcessInfo(JSContext
* aCx
, Promise
** aResult
) {
1494 NS_ENSURE_ARG_POINTER(aResult
);
1497 if (!XRE_IsParentProcess()) {
1498 return NS_ERROR_FAILURE
;
1501 nsIGlobalObject
* global
= xpc::CurrentNativeGlobal(aCx
);
1502 if (NS_WARN_IF(!global
)) {
1503 return NS_ERROR_FAILURE
;
1507 RefPtr
<Promise
> promise
= Promise::Create(global
, erv
);
1508 if (NS_WARN_IF(erv
.Failed())) {
1509 return erv
.StealNSResult();
1512 if (!mProcessInfoPromise
) {
1513 RefPtr
<nsISerialEventTarget
> backgroundET
= GetBackgroundTarget();
1515 mProcessInfoPromise
= InvokeAsync(backgroundET
, __func__
, []() {
1517 nsresult rv
= CollectProcessInfo(info
);
1518 if (NS_SUCCEEDED(rv
)) {
1519 return ProcessInfoPromise::CreateAndResolve(info
, __func__
);
1521 return ProcessInfoPromise::CreateAndReject(rv
, __func__
);
1525 // Chain the new promise to the extant mozpromise
1526 RefPtr
<Promise
> capturedPromise
= promise
;
1527 mProcessInfoPromise
->Then(
1528 GetMainThreadSerialEventTarget(), __func__
,
1529 [capturedPromise
](const ProcessInfo
& info
) {
1531 if (NS_WARN_IF(!jsapi
.Init(capturedPromise
->GetGlobalObject()))) {
1532 capturedPromise
->MaybeReject(NS_ERROR_UNEXPECTED
);
1535 JSContext
* cx
= jsapi
.cx();
1536 JS::Rooted
<JS::Value
> val(
1537 cx
, JS::ObjectValue(*GetJSObjForProcessInfo(cx
, info
)));
1538 capturedPromise
->MaybeResolve(val
);
1540 [capturedPromise
](const nsresult rv
) {
1541 // Resolve with null when installYear is not available from the system
1542 capturedPromise
->MaybeResolve(JS::NullHandleValue
);
1545 promise
.forget(aResult
);