no bug - Bumping Firefox l10n changesets r=release a=l10n-bump DONTBUILD CLOSED TREE
[gecko.git] / xpcom / base / nsSystemInfo.cpp
blob2b567bda98d72d59ec889cf2f592aa67301b70f7
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"
11 #include "prsystem.h"
12 #include "prio.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"
19 #include "jsapi.h"
20 #include "js/PropertyAndElement.h" // JS_SetProperty
21 #include "mozilla/dom/Promise.h"
23 #ifdef XP_WIN
24 # include <comutil.h>
25 # include <time.h>
26 # ifndef __MINGW32__
27 # include <iwscapi.h>
28 # endif // __MINGW32__
29 # include <windows.h>
30 # include <winioctl.h>
31 # ifndef __MINGW32__
32 # include <wrl.h>
33 # include <wscapi.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"
45 #endif
47 #ifdef XP_MACOSX
48 # include "MacHelpers.h"
49 #endif
51 #ifdef MOZ_WIDGET_GTK
52 # include <gtk/gtk.h>
53 # include <dlfcn.h>
54 # include "mozilla/WidgetUtilsGtk.h"
55 #endif
57 #if defined(XP_LINUX) && !defined(ANDROID)
58 # include <unistd.h>
59 # include <fstream>
60 # include "mozilla/Tokenizer.h"
61 # include "mozilla/widget/LSBUtils.h"
62 # include "nsCharSeparatedTokenizer.h"
64 # include <map>
65 # include <string>
66 #endif
68 #ifdef MOZ_WIDGET_ANDROID
69 # include "AndroidBuild.h"
70 # include "mozilla/java/GeckoAppShellWrappers.h"
71 # include "mozilla/jni/Utils.h"
72 #endif
74 #ifdef XP_MACOSX
75 # include <sys/sysctl.h>
76 #endif
78 #if defined(XP_LINUX) && defined(MOZ_SANDBOX)
79 # include "mozilla/SandboxInfo.h"
80 #endif
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;
91 #if defined(XP_WIN)
92 # define RuntimeClass_Windows_System_Profile_WindowsIntegrityPolicy \
93 L"Windows.System.Profile.WindowsIntegrityPolicy"
94 # ifndef __MINGW32__
95 using namespace Microsoft::WRL;
96 using namespace Microsoft::WRL::Wrappers;
97 using namespace ABI::Windows::Foundation;
98 # endif // __MINGW32__
99 #endif
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;
122 #endif
124 #ifdef XP_WIN
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);
131 *physical_cpus = 0;
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)];
145 } else {
146 return;
150 for (size_t i = 0;
151 i < return_length / sizeof(SYSTEM_LOGICAL_PROCESSOR_INFORMATION); ++i) {
152 if (infos[i].Relationship == RelationProcessorCore) {
153 ++*physical_cpus;
154 } else if (infos[i].Relationship == RelationCache) {
155 // Only care about L2 and L3 cache
156 switch (infos[i].Cache.Level) {
157 case 2:
158 *cache_size_L2 = static_cast<int>(infos[i].Cache.Size / 1024);
159 break;
160 case 3:
161 *cache_size_L3 = static_cast<int>(infos[i].Cache.Size / 1024);
162 break;
163 default:
164 break;
168 if (infos != &info_buffer[0]) {
169 delete[] infos;
171 return;
173 #endif
175 #if defined(XP_WIN)
176 namespace {
177 static nsresult GetFolderDiskInfo(nsIFile* file, FolderDiskInfo& info) {
178 info.model.Truncate();
179 info.revision.Truncate();
180 info.isSSD = false;
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)};
208 DWORD bytesRead = 0;
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)) {
219 free(deviceOutput);
220 return NS_ERROR_FAILURE;
223 queryParameters.PropertyId = StorageDeviceTrimProperty;
224 bytesRead = 0;
225 bool isSSD = false;
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) {
231 isSSD = true;
235 if (isSSD) {
236 // Get Seek Penalty
237 queryParameters.PropertyId = StorageDeviceSeekPenaltyProperty;
238 bytesRead = 0;
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) {
248 isSSD = false;
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) {
257 info.model =
258 reinterpret_cast<char*>(deviceOutput) + deviceOutput->VendorIdOffset;
260 if (deviceOutput->ProductIdOffset) {
261 info.model +=
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();
270 info.isSSD = isSSD;
271 free(deviceOutput);
272 return NS_OK;
275 static nsresult CollectDiskInfo(nsIFile* greDir, nsIFile* winDir,
276 nsIFile* profDir, DiskInfo& info) {
277 nsresult rv = GetFolderDiskInfo(greDir, info.binary);
278 if (NS_FAILED(rv)) {
279 return rv;
281 rv = GetFolderDiskInfo(winDir, info.system);
282 if (NS_FAILED(rv)) {
283 return rv;
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);
300 DWORD type = 0;
301 time_t raw_time = 0;
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;
315 tm time;
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));
325 if (!scm) {
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
333 // 10 build 1809.
334 nsAutoServiceHandle hService(OpenService(scm, L"SysMain", GENERIC_READ));
336 if (hService) {
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;
399 return NS_OK;
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);
409 if (!numChars) {
410 return NS_ERROR_FAILURE;
412 // Now get the string for real
413 aCountryCode.SetLength(numChars);
414 numChars =
415 GetGeoInfoW(geoid, GEO_ISO2, char16ptr_t(aCountryCode.BeginWriting()),
416 aCountryCode.Length(), 0);
417 if (!numChars) {
418 return NS_ERROR_FAILURE;
421 // numChars includes null terminator
422 aCountryCode.Truncate(numChars - 1);
423 return NS_OK;
426 } // namespace
428 # ifndef __MINGW32__
430 static HRESULT EnumWSCProductList(
431 nsAString& aOutput, mozilla::NotNull<IWSCProductList*> aProdList) {
432 MOZ_ASSERT(aOutput.IsEmpty());
434 LONG count;
435 HRESULT hr = aProdList->get_Count(&count);
436 if (FAILED(hr)) {
437 return hr;
440 for (LONG index = 0; index < count; ++index) {
441 RefPtr<IWscProduct> product;
442 hr = aProdList->get_Item(index, getter_AddRefs(product));
443 if (FAILED(hr)) {
444 return hr;
447 WSC_SECURITY_PRODUCT_STATE state;
448 hr = product->get_ProductState(&state);
449 if (FAILED(hr)) {
450 return hr;
453 // We only care about products that are active
454 if (state == WSC_SECURITY_PRODUCT_STATE_OFF ||
455 state == WSC_SECURITY_PRODUCT_STATE_SNOOZED) {
456 continue;
459 _bstr_t bName;
460 hr = product->get_ProductName(bName.GetAddress());
461 if (FAILED(hr)) {
462 return hr;
465 if (!aOutput.IsEmpty()) {
466 aOutput.AppendLiteral(u";");
469 aOutput.Append((wchar_t*)bName, bName.length());
472 return S_OK;
475 static nsresult GetWindowsSecurityCenterInfo(nsAString& aAVInfo,
476 nsAString& aAntiSpyInfo,
477 nsAString& aFirewallInfo) {
478 aAVInfo.Truncate();
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};
500 static_assert(
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);
505 ++index) {
506 RefPtr<IWSCProductList> prodList;
507 HRESULT hr = ::CoCreateInstance(clsid, nullptr, CLSCTX_INPROC_SERVER, iid,
508 getter_AddRefs(prodList));
509 if (FAILED(hr)) {
510 return NS_ERROR_NOT_AVAILABLE;
513 hr = prodList->Initialize(providerTypes[index]);
514 if (FAILED(hr)) {
515 return NS_ERROR_UNEXPECTED;
518 hr = EnumWSCProductList(*outputs[index],
519 mozilla::WrapNotNull(prodList.get()));
520 if (FAILED(hr)) {
521 return NS_ERROR_UNEXPECTED;
525 return NS_OK;
528 # endif // __MINGW32__
530 #endif // defined(XP_WIN)
532 #ifdef XP_MACOSX
533 static nsresult GetAppleModelId(nsAutoCString& aModelId) {
534 size_t numChars = 0;
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);
540 result =
541 sysctlbyname("hw.model", aModelId.BeginWriting(), &numChars, nullptr, 0);
542 if (result != 0) {
543 return NS_ERROR_FAILURE;
545 // numChars includes null terminator
546 aModelId.Truncate(numChars - 1);
547 return NS_OK;
550 static nsresult ProcessIsRosettaTranslated(bool& isRosetta) {
551 # if defined(__aarch64__)
552 // There is no need to call sysctlbyname() if we are running as arm64.
553 isRosetta = false;
554 # else
555 int ret = 0;
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");
561 isRosetta = false;
562 } else {
563 isRosetta = (ret == 1);
565 # endif
566 return NS_OK;
568 #endif
570 using namespace mozilla;
572 nsSystemInfo::nsSystemInfo() = default;
574 nsSystemInfo::~nsSystemInfo() = default;
576 // CPU-specific information.
577 static const struct PropItems {
578 const char* name;
579 bool (*propfun)(void);
580 } cpuPropItems[] = {
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;
602 int cpuSpeed = -1;
603 int cpuFamily = -1;
604 int cpuModel = -1;
605 int cpuStepping = -1;
606 int logicalCPUs = -1;
607 int physicalCPUs = -1;
608 int cacheSizeL2 = -1;
609 int cacheSizeL3 = -1;
611 #if defined(XP_WIN)
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;
620 BOOL gotWow64Value;
621 if (iwp2) {
622 gotWow64Value = iwp2(GetCurrentProcess(), &processMachine, &nativeMachine);
623 if (gotWow64Value) {
624 isWow64 = (processMachine != IMAGE_FILE_MACHINE_UNKNOWN);
626 } else {
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");
636 if (gotWow64Value) {
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);
644 // S Mode
646 # ifndef __MINGW32__
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(
655 HStringReference(
656 RuntimeClass_Windows_System_Profile_WindowsIntegrityPolicy)
657 .Get(),
658 &wip);
659 if (SUCCEEDED(hr)) {
660 // info.isWindowsSMode ends up true if Windows is in S mode, otherwise
661 // false
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__
670 // CPU speed
671 HKEY key;
672 static const WCHAR keyName[] =
673 L"HARDWARE\\DESCRIPTION\\System\\CentralProcessor\\0";
675 if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, keyName, 0, KEY_QUERY_VALUE, &key) ==
676 ERROR_SUCCESS) {
677 DWORD data, len, vtype;
678 len = sizeof(data);
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);
712 RegCloseKey(key);
715 // Other CPU attributes:
716 SYSTEM_INFO si;
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)
727 // CPU speed
728 uint64_t sysctlValue64 = 0;
729 uint32_t sysctlValue32 = 0;
730 size_t len = 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;
774 delete[] 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"
810 Tokenizer::Token t;
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"
820 Tokenizer::Token t;
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"
830 Tokenizer::Token t;
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"
840 Tokenizer::Token t;
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.
851 std::ifstream input(
852 "/sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_max_freq");
853 std::string line;
854 if (getline(input, line)) {
855 Tokenizer::Token t;
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");
867 std::string line;
868 if (getline(input, line)) {
869 Tokenizer::Token t;
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");
881 std::string line;
882 if (getline(input, line)) {
883 Tokenizer::Token t;
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();
893 #else
894 info.cpuCount = PR_GetNumberOfProcessors();
895 #endif
897 if (cpuSpeed >= 0) {
898 info.cpuSpeed = cpuSpeed;
899 } else {
900 info.cpuSpeed = 0;
902 if (!cpuVendor.IsEmpty()) {
903 info.cpuVendor = cpuVendor;
905 if (!cpuName.IsEmpty()) {
906 info.cpuName = cpuName;
908 if (cpuFamily >= 0) {
909 info.cpuFamily = cpuFamily;
911 if (cpuModel >= 0) {
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;
932 return NS_OK;
935 #if defined(__MINGW32__)
936 WINBASEAPI
937 BOOL WINAPI IsUserCetAvailableInEnvironment(_In_ DWORD UserCetEnvironment);
939 # define USER_CET_ENVIRONMENT_WIN32_PROCESS 0x00000000
940 #endif
942 nsresult nsSystemInfo::Init() {
943 // check that it is called from the main thread on all platforms.
944 MOZ_ASSERT(NS_IsMainThread());
946 nsresult rv;
948 static const struct {
949 PRSysInfo cmd;
950 const char* name;
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))) {
962 return rv;
964 } else {
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);
980 #else
981 SetUint32Property(u"archbits"_ns, 32);
982 #endif
984 uint64_t virtualMem = 0;
986 #if defined(XP_WIN)
987 // Virtual memory:
988 MEMORYSTATUSEX memStat;
989 memStat.dwLength = sizeof(memStat);
990 if (GlobalMemoryStatusEx(&memStat)) {
991 virtualMem = memStat.ullTotalVirtual;
993 #endif
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))) {
1000 return rv;
1004 #ifdef XP_WIN
1005 bool isMinGW =
1006 # ifdef __MINGW32__
1007 true;
1008 # else
1009 false;
1010 # endif
1011 rv = SetPropertyAsBool(u"isMinGW"_ns, !!isMinGW);
1012 if (NS_WARN_IF(NS_FAILED(rv))) {
1013 return rv;
1016 boolean hasPackageIdentity = widget::WinUtils::HasPackageIdentity();
1018 rv = SetPropertyAsBool(u"hasWinPackageId"_ns, hasPackageIdentity);
1019 if (NS_WARN_IF(NS_FAILED(rv))) {
1020 return rv;
1023 rv = SetPropertyAsAString(u"winPackageFamilyName"_ns,
1024 widget::WinUtils::GetPackageFamilyName());
1025 if (NS_WARN_IF(NS_FAILED(rv))) {
1026 return rv;
1029 rv = SetPropertyAsBool(u"isPackagedApp"_ns, hasPackageIdentity);
1030 if (NS_WARN_IF(NS_FAILED(rv))) {
1031 return rv;
1034 # ifndef __MINGW32__
1035 nsAutoString avInfo, antiSpyInfo, firewallInfo;
1036 if (NS_SUCCEEDED(
1037 GetWindowsSecurityCenterInfo(avInfo, antiSpyInfo, firewallInfo))) {
1038 if (!avInfo.IsEmpty()) {
1039 rv = SetPropertyAsAString(u"registeredAntiVirus"_ns, avInfo);
1040 if (NS_WARN_IF(NS_FAILED(rv))) {
1041 return rv;
1045 if (!antiSpyInfo.IsEmpty()) {
1046 rv = SetPropertyAsAString(u"registeredAntiSpyware"_ns, antiSpyInfo);
1047 if (NS_WARN_IF(NS_FAILED(rv))) {
1048 return rv;
1052 if (!firewallInfo.IsEmpty()) {
1053 rv = SetPropertyAsAString(u"registeredFirewall"_ns, firewallInfo);
1054 if (NS_WARN_IF(NS_FAILED(rv))) {
1055 return 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))) {
1069 return rv;
1072 nsString pointerExplanation;
1073 widget::WinUtils::GetPointerExplanation(&pointerExplanation);
1074 rv = SetPropertyAsAString(u"pointingDevices"_ns, pointerExplanation);
1075 if (NS_WARN_IF(NS_FAILED(rv))) {
1076 return rv;
1079 #endif
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);
1087 bool isRosetta;
1088 if (NS_SUCCEEDED(ProcessIsRosettaTranslated(isRosetta))) {
1089 rv = SetPropertyAsBool(u"rosettaStatus"_ns, isRosetta);
1090 NS_ENSURE_SUCCESS(rv, rv);
1092 #endif
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.
1104 char gtkver[64];
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));
1117 # ifndef MOZ_TSAN
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";
1123 if (libpulse) {
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);
1134 if (libpulse) {
1135 dlclose(libpulse);
1137 # endif
1139 rv = SetPropertyAsACString(u"secondaryLibrary"_ns, secondaryLibrary);
1140 if (NS_WARN_IF(NS_FAILED(rv))) {
1141 return rv;
1143 rv = SetPropertyAsBool(u"isPackagedApp"_ns,
1144 widget::IsRunningUnderFlatpakOrSnap() ||
1145 widget::IsPackagedAppFileExists());
1146 if (NS_WARN_IF(NS_FAILED(rv))) {
1147 return rv;
1149 #endif
1151 #ifdef MOZ_WIDGET_ANDROID
1152 AndroidSystemInfo info;
1153 GetAndroidSystemInfo(&info);
1154 SetupAndroidInfo(info);
1155 #endif
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);
1163 #endif
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
1186 return NS_OK;
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"
1199 /* static */
1200 void nsSystemInfo::GetAndroidSystemInfo(AndroidSystemInfo* aInfo) {
1201 if (!jni::IsAvailable()) {
1202 // called from xpcshell etc.
1203 aInfo->sdk_version() = 0;
1204 return;
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());
1219 int major_version;
1220 int minor_version;
1221 int bugfix_version;
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);
1226 } else {
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
1247 // kernel_version.
1248 nsAutoString str;
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");
1266 if (aValue > 0) {
1267 #ifdef DEBUG
1268 nsresult rv =
1269 #endif
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.
1279 #ifdef DEBUG
1280 nsresult rv =
1281 #endif
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");
1289 if (aValue > 0) {
1290 #ifdef DEBUG
1291 nsresult rv =
1292 #endif
1293 SetPropertyAsUint64(aPropertyName, aValue);
1294 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "Unable to set property");
1298 #ifdef XP_WIN
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));
1304 if (!jsInfo) {
1305 return false;
1308 JSString* strModel =
1309 JS_NewStringCopyN(aCx, info.model.get(), info.model.Length());
1310 if (!strModel) {
1311 return false;
1313 JS::Rooted<JS::Value> valModel(aCx, JS::StringValue(strModel));
1314 if (!JS_SetProperty(aCx, jsInfo, "model", valModel)) {
1315 return false;
1318 JSString* strRevision =
1319 JS_NewStringCopyN(aCx, info.revision.get(), info.revision.Length());
1320 if (!strRevision) {
1321 return false;
1323 JS::Rooted<JS::Value> valRevision(aCx, JS::StringValue(strRevision));
1324 if (!JS_SetProperty(aCx, jsInfo, "revision", valRevision)) {
1325 return false;
1328 JSString* strSSD = JS_NewStringCopyZ(aCx, info.isSSD ? "SSD" : "HDD");
1329 if (!strSSD) {
1330 return false;
1332 JS::Rooted<JS::Value> valSSD(aCx, JS::StringValue(strSSD));
1333 if (!JS_SetProperty(aCx, jsInfo, "type", valSSD)) {
1334 return false;
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);
1354 return jsInfo;
1357 #endif
1359 JSObject* GetJSObjForProcessInfo(JSContext* aCx, const ProcessInfo& info) {
1360 JS::Rooted<JSObject*> jsInfo(aCx, JS_NewPlainObject(aCx));
1362 #if defined(XP_WIN)
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);
1372 #endif
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);
1385 JSString* strName =
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);
1408 return jsInfo;
1411 RefPtr<nsISerialEventTarget> nsSystemInfo::GetBackgroundTarget() {
1412 if (!mBackgroundET) {
1413 MOZ_ALWAYS_SUCCEEDS(NS_CreateBackgroundTaskQueue(
1414 "SystemInfoThread", getter_AddRefs(mBackgroundET)));
1416 return mBackgroundET;
1419 NS_IMETHODIMP
1420 nsSystemInfo::GetOsInfo(JSContext* aCx, Promise** aResult) {
1421 NS_ENSURE_ARG_POINTER(aResult);
1422 *aResult = nullptr;
1423 if (!XRE_IsParentProcess()) {
1424 return NS_ERROR_FAILURE;
1426 #if defined(XP_WIN)
1427 nsIGlobalObject* global = xpc::CurrentNativeGlobal(aCx);
1428 if (NS_WARN_IF(!global)) {
1429 return NS_ERROR_FAILURE;
1432 ErrorResult erv;
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__, []() {
1442 OSInfo info;
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) {
1456 AutoJSAPI jsapi;
1457 if (NS_WARN_IF(!jsapi.Init(capturedPromise->GetGlobalObject()))) {
1458 capturedPromise->MaybeReject(NS_ERROR_UNEXPECTED);
1459 return;
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);
1472 #endif
1473 return NS_OK;
1476 NS_IMETHODIMP
1477 nsSystemInfo::GetDiskInfo(JSContext* aCx, Promise** aResult) {
1478 NS_ENSURE_ARG_POINTER(aResult);
1479 *aResult = nullptr;
1480 if (!XRE_IsParentProcess()) {
1481 return NS_ERROR_FAILURE;
1483 #ifdef XP_WIN
1484 nsIGlobalObject* global = xpc::CurrentNativeGlobal(aCx);
1485 if (NS_WARN_IF(!global)) {
1486 return NS_ERROR_FAILURE;
1488 ErrorResult erv;
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)) {
1501 return rv;
1503 rv = NS_GetSpecialDirectory(NS_WIN_WINDOWS_DIR, getter_AddRefs(winDir));
1504 if (NS_FAILED(rv)) {
1505 return rv;
1507 rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR,
1508 getter_AddRefs(profDir));
1509 if (NS_FAILED(rv)) {
1510 return rv;
1513 mDiskInfoPromise =
1514 InvokeAsync(backgroundET, __func__, [greDir, winDir, profDir]() {
1515 DiskInfo info;
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) {
1529 AutoJSAPI jsapi;
1530 if (NS_WARN_IF(!jsapi.Init(capturedPromise->GetGlobalObject()))) {
1531 capturedPromise->MaybeReject(NS_ERROR_UNEXPECTED);
1532 return;
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);
1545 return;
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);
1556 #endif
1557 return NS_OK;
1560 NS_IMPL_ISUPPORTS_INHERITED(nsSystemInfo, nsHashPropertyBag, nsISystemInfo)
1562 NS_IMETHODIMP
1563 nsSystemInfo::GetCountryCode(JSContext* aCx, Promise** aResult) {
1564 NS_ENSURE_ARG_POINTER(aResult);
1565 *aResult = nullptr;
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;
1576 ErrorResult erv;
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;
1587 # ifdef XP_MACOSX
1588 nsresult rv = GetSelectedCityInfo(countryCode);
1589 # endif
1590 # ifdef XP_WIN
1591 nsresult rv = CollectCountryCode(countryCode);
1592 # endif
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) {
1605 AutoJSAPI jsapi;
1606 if (NS_WARN_IF(!jsapi.Init(capturedPromise->GetGlobalObject()))) {
1607 capturedPromise->MaybeReject(NS_ERROR_UNEXPECTED);
1608 return;
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);
1623 #endif
1624 return NS_OK;
1627 NS_IMETHODIMP
1628 nsSystemInfo::GetProcessInfo(JSContext* aCx, Promise** aResult) {
1629 NS_ENSURE_ARG_POINTER(aResult);
1630 *aResult = nullptr;
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;
1641 ErrorResult erv;
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__, []() {
1651 ProcessInfo info;
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) {
1665 AutoJSAPI jsapi;
1666 if (NS_WARN_IF(!jsapi.Init(capturedPromise->GetGlobalObject()))) {
1667 capturedPromise->MaybeReject(NS_ERROR_UNEXPECTED);
1668 return;
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);
1682 return NS_OK;