Bug 1690340 - Part 4: Insert the "Page Source" before the "Extensions for Developers...
[gecko.git] / xpcom / base / nsSystemInfo.cpp
blob0fb2655d2cf2db4031805e8229ac6e927374d376
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/Sprintf.h"
17 #include "jsapi.h"
18 #include "mozilla/dom/Promise.h"
20 #ifdef XP_WIN
21 # include <comutil.h>
22 # include <time.h>
23 # ifndef __MINGW32__
24 # include <iwscapi.h>
25 # endif // __MINGW32__
26 # include <windows.h>
27 # include <winioctl.h>
28 # ifndef __MINGW32__
29 # include <wscapi.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"
38 #endif
40 #ifdef XP_MACOSX
41 # include "MacHelpers.h"
42 #endif
44 #ifdef MOZ_WIDGET_GTK
45 # include <gtk/gtk.h>
46 # include <dlfcn.h>
47 #endif
49 #if defined(XP_LINUX) && !defined(ANDROID)
50 # include <unistd.h>
51 # include <fstream>
52 # include "mozilla/Tokenizer.h"
53 # include "nsCharSeparatedTokenizer.h"
55 # include <map>
56 # include <string>
57 #endif
59 #ifdef MOZ_WIDGET_ANDROID
60 # include "AndroidBuild.h"
61 # include "mozilla/java/GeckoAppShellWrappers.h"
62 # include "mozilla/jni/Utils.h"
63 #endif
65 #ifdef XP_MACOSX
66 # include <sys/sysctl.h>
67 #endif
69 #if defined(XP_LINUX) && defined(MOZ_SANDBOX)
70 # include "mozilla/SandboxInfo.h"
71 #endif
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;
103 #endif
105 #ifdef XP_WIN
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);
112 *physical_cpus = 0;
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)];
126 } else {
127 return;
131 for (size_t i = 0;
132 i < return_length / sizeof(SYSTEM_LOGICAL_PROCESSOR_INFORMATION); ++i) {
133 if (infos[i].Relationship == RelationProcessorCore) {
134 ++*physical_cpus;
135 } else if (infos[i].Relationship == RelationCache) {
136 // Only care about L2 and L3 cache
137 switch (infos[i].Cache.Level) {
138 case 2:
139 *cache_size_L2 = static_cast<int>(infos[i].Cache.Size / 1024);
140 break;
141 case 3:
142 *cache_size_L3 = static_cast<int>(infos[i].Cache.Size / 1024);
143 break;
144 default:
145 break;
149 if (infos != &info_buffer[0]) {
150 delete[] infos;
152 return;
154 #endif
156 #if defined(XP_WIN)
157 namespace {
158 static nsresult GetFolderDiskInfo(nsIFile* file, FolderDiskInfo& info) {
159 info.model.Truncate();
160 info.revision.Truncate();
161 info.isSSD = false;
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)};
189 DWORD bytesRead = 0;
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)) {
200 free(deviceOutput);
201 return NS_ERROR_FAILURE;
204 queryParameters.PropertyId = StorageDeviceTrimProperty;
205 bytesRead = 0;
206 bool isSSD = false;
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) {
212 isSSD = true;
216 if (isSSD) {
217 // Get Seek Penalty
218 queryParameters.PropertyId = StorageDeviceSeekPenaltyProperty;
219 bytesRead = 0;
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) {
229 isSSD = false;
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) {
238 info.model =
239 reinterpret_cast<char*>(deviceOutput) + deviceOutput->VendorIdOffset;
241 if (deviceOutput->ProductIdOffset) {
242 info.model +=
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();
251 info.isSSD = isSSD;
252 free(deviceOutput);
253 return NS_OK;
256 static nsresult CollectDiskInfo(nsIFile* greDir, nsIFile* winDir,
257 nsIFile* profDir, DiskInfo& info) {
258 nsresult rv = GetFolderDiskInfo(greDir, info.binary);
259 if (NS_FAILED(rv)) {
260 return rv;
262 rv = GetFolderDiskInfo(winDir, info.system);
263 if (NS_FAILED(rv)) {
264 return rv;
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);
281 DWORD type = 0;
282 time_t raw_time = 0;
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;
296 tm time;
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));
306 if (!scm) {
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
314 // 10 build 1809.
315 nsAutoServiceHandle hService(OpenService(scm, L"SysMain", GENERIC_READ));
317 if (hService) {
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;
380 return NS_OK;
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);
390 if (!numChars) {
391 return NS_ERROR_FAILURE;
393 // Now get the string for real
394 aCountryCode.SetLength(numChars);
395 numChars =
396 GetGeoInfoW(geoid, GEO_ISO2, char16ptr_t(aCountryCode.BeginWriting()),
397 aCountryCode.Length(), 0);
398 if (!numChars) {
399 return NS_ERROR_FAILURE;
402 // numChars includes null terminator
403 aCountryCode.Truncate(numChars - 1);
404 return NS_OK;
407 } // namespace
409 # ifndef __MINGW32__
411 static HRESULT EnumWSCProductList(nsAString& aOutput,
412 NotNull<IWSCProductList*> aProdList) {
413 MOZ_ASSERT(aOutput.IsEmpty());
415 LONG count;
416 HRESULT hr = aProdList->get_Count(&count);
417 if (FAILED(hr)) {
418 return hr;
421 for (LONG index = 0; index < count; ++index) {
422 RefPtr<IWscProduct> product;
423 hr = aProdList->get_Item(index, getter_AddRefs(product));
424 if (FAILED(hr)) {
425 return hr;
428 WSC_SECURITY_PRODUCT_STATE state;
429 hr = product->get_ProductState(&state);
430 if (FAILED(hr)) {
431 return hr;
434 // We only care about products that are active
435 if (state == WSC_SECURITY_PRODUCT_STATE_OFF ||
436 state == WSC_SECURITY_PRODUCT_STATE_SNOOZED) {
437 continue;
440 _bstr_t bName;
441 hr = product->get_ProductName(bName.GetAddress());
442 if (FAILED(hr)) {
443 return hr;
446 if (!aOutput.IsEmpty()) {
447 aOutput.AppendLiteral(u";");
450 aOutput.Append((wchar_t*)bName, bName.length());
453 return S_OK;
456 static nsresult GetWindowsSecurityCenterInfo(nsAString& aAVInfo,
457 nsAString& aAntiSpyInfo,
458 nsAString& aFirewallInfo) {
459 aAVInfo.Truncate();
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));
488 if (FAILED(hr)) {
489 return NS_ERROR_NOT_AVAILABLE;
492 hr = prodList->Initialize(providerTypes[index]);
493 if (FAILED(hr)) {
494 return NS_ERROR_UNEXPECTED;
497 hr = EnumWSCProductList(*outputs[index], WrapNotNull(prodList.get()));
498 if (FAILED(hr)) {
499 return NS_ERROR_UNEXPECTED;
503 return NS_OK;
506 # endif // __MINGW32__
508 #endif // defined(XP_WIN)
510 #ifdef XP_MACOSX
511 static nsresult GetAppleModelId(nsAutoCString& aModelId) {
512 size_t numChars = 0;
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);
518 result =
519 sysctlbyname("hw.model", aModelId.BeginWriting(), &numChars, nullptr, 0);
520 if (result != 0) {
521 return NS_ERROR_FAILURE;
523 // numChars includes null terminator
524 aModelId.Truncate(numChars - 1);
525 return NS_OK;
528 static nsresult ProcessIsRosettaTranslated(bool& isRosetta) {
529 # if defined(__aarch64__)
530 // There is no need to call sysctlbyname() if we are running as arm64.
531 isRosetta = false;
532 # else
533 int ret = 0;
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");
539 isRosetta = false;
540 } else {
541 isRosetta = (ret == 1);
543 # endif
544 return NS_OK;
546 #endif
548 using namespace mozilla;
550 nsSystemInfo::nsSystemInfo() = default;
552 nsSystemInfo::~nsSystemInfo() = default;
554 // CPU-specific information.
555 static const struct PropItems {
556 const char* name;
557 bool (*propfun)(void);
558 } cpuPropItems[] = {
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;
579 int cpuSpeed = -1;
580 int cpuFamily = -1;
581 int cpuModel = -1;
582 int cpuStepping = -1;
583 int logicalCPUs = -1;
584 int physicalCPUs = -1;
585 int cacheSizeL2 = -1;
586 int cacheSizeL3 = -1;
588 #if defined(XP_WIN)
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;
597 BOOL gotWow64Value;
598 if (iwp2) {
599 gotWow64Value = iwp2(GetCurrentProcess(), &processMachine, &nativeMachine);
600 if (gotWow64Value) {
601 isWow64 = (processMachine != IMAGE_FILE_MACHINE_UNKNOWN);
603 } else {
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");
613 if (gotWow64Value) {
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);
621 // CPU speed
622 HKEY key;
623 static const WCHAR keyName[] =
624 L"HARDWARE\\DESCRIPTION\\System\\CentralProcessor\\0";
626 if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, keyName, 0, KEY_QUERY_VALUE, &key) ==
627 ERROR_SUCCESS) {
628 DWORD data, len, vtype;
629 len = sizeof(data);
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);
649 RegCloseKey(key);
652 // Other CPU attributes:
653 SYSTEM_INFO si;
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)
664 // CPU speed
665 uint64_t sysctlValue64 = 0;
666 uint32_t sysctlValue32 = 0;
667 size_t len = 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"
736 Tokenizer::Token t;
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"
746 Tokenizer::Token t;
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"
756 Tokenizer::Token t;
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"
766 Tokenizer::Token t;
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.
777 std::ifstream input(
778 "/sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_max_freq");
779 std::string line;
780 if (getline(input, line)) {
781 Tokenizer::Token t;
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");
793 std::string line;
794 if (getline(input, line)) {
795 Tokenizer::Token t;
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");
807 std::string line;
808 if (getline(input, line)) {
809 Tokenizer::Token t;
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();
819 #else
820 info.cpuCount = PR_GetNumberOfProcessors();
821 #endif
823 if (cpuSpeed >= 0) {
824 info.cpuSpeed = cpuSpeed;
825 } else {
826 info.cpuSpeed = 0;
828 if (!cpuVendor.IsEmpty()) {
829 info.cpuVendor = cpuVendor;
831 if (cpuFamily >= 0) {
832 info.cpuFamily = cpuFamily;
834 if (cpuModel >= 0) {
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;
855 return NS_OK;
858 #if defined(XP_WIN) && (_WIN32_WINNT < 0x0A00)
859 WINBASEAPI
860 BOOL WINAPI IsUserCetAvailableInEnvironment(_In_ DWORD UserCetEnvironment);
862 # define USER_CET_ENVIRONMENT_WIN32_PROCESS 0x00000000
863 #endif
865 nsresult nsSystemInfo::Init() {
866 // check that it is called from the main thread on all platforms.
867 MOZ_ASSERT(NS_IsMainThread());
869 nsresult rv;
871 static const struct {
872 PRSysInfo cmd;
873 const char* name;
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))) {
885 return rv;
887 } else {
888 NS_WARNING("PR_GetSystemInfo failed");
892 rv = SetPropertyAsBool(NS_ConvertASCIItoUTF16("hasWindowsTouchInterface"),
893 false);
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;
905 #if defined(XP_WIN)
906 // Virtual memory:
907 MEMORYSTATUSEX memStat;
908 memStat.dwLength = sizeof(memStat);
909 if (GlobalMemoryStatusEx(&memStat)) {
910 virtualMem = memStat.ullTotalVirtual;
912 #endif
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))) {
919 return rv;
923 #ifdef XP_WIN
924 bool isMinGW =
925 # ifdef __MINGW32__
926 true;
927 # else
928 false;
929 # endif
930 rv = SetPropertyAsBool(u"isMinGW"_ns, !!isMinGW);
931 if (NS_WARN_IF(NS_FAILED(rv))) {
932 return rv;
935 # ifndef __MINGW32__
936 nsAutoString avInfo, antiSpyInfo, firewallInfo;
937 if (NS_SUCCEEDED(
938 GetWindowsSecurityCenterInfo(avInfo, antiSpyInfo, firewallInfo))) {
939 if (!avInfo.IsEmpty()) {
940 rv = SetPropertyAsAString(u"registeredAntiVirus"_ns, avInfo);
941 if (NS_WARN_IF(NS_FAILED(rv))) {
942 return rv;
946 if (!antiSpyInfo.IsEmpty()) {
947 rv = SetPropertyAsAString(u"registeredAntiSpyware"_ns, antiSpyInfo);
948 if (NS_WARN_IF(NS_FAILED(rv))) {
949 return rv;
953 if (!firewallInfo.IsEmpty()) {
954 rv = SetPropertyAsAString(u"registeredFirewall"_ns, firewallInfo);
955 if (NS_WARN_IF(NS_FAILED(rv))) {
956 return 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))) {
970 return rv;
973 #endif
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);
981 bool isRosetta;
982 if (NS_SUCCEEDED(ProcessIsRosettaTranslated(isRosetta))) {
983 rv = SetPropertyAsBool(u"rosettaStatus"_ns, isRosetta);
984 NS_ENSURE_SUCCESS(rv, rv);
986 #endif
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.
992 char gtkver[64];
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));
1005 # ifndef MOZ_TSAN
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";
1011 if (libpulse) {
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);
1022 if (libpulse) {
1023 dlclose(libpulse);
1025 # endif
1027 rv = SetPropertyAsACString(u"secondaryLibrary"_ns, secondaryLibrary);
1028 if (NS_WARN_IF(NS_FAILED(rv))) {
1029 return rv;
1031 #endif
1033 #ifdef MOZ_WIDGET_ANDROID
1034 AndroidSystemInfo info;
1035 GetAndroidSystemInfo(&info);
1036 SetupAndroidInfo(info);
1037 #endif
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
1060 return NS_OK;
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"
1073 /* static */
1074 void nsSystemInfo::GetAndroidSystemInfo(AndroidSystemInfo* aInfo) {
1075 if (!jni::IsAvailable()) {
1076 // called from xpcshell etc.
1077 aInfo->sdk_version() = 0;
1078 return;
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());
1093 int major_version;
1094 int minor_version;
1095 int bugfix_version;
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);
1100 } else {
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
1121 // kernel_version.
1122 nsAutoString str;
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");
1140 if (aValue > 0) {
1141 #ifdef DEBUG
1142 nsresult rv =
1143 #endif
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.
1153 #ifdef DEBUG
1154 nsresult rv =
1155 #endif
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");
1163 if (aValue > 0) {
1164 #ifdef DEBUG
1165 nsresult rv =
1166 #endif
1167 SetPropertyAsUint64(aPropertyName, aValue);
1168 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "Unable to set property");
1172 #ifdef XP_WIN
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));
1178 if (!jsInfo) {
1179 return false;
1182 JSString* strModel =
1183 JS_NewStringCopyN(aCx, info.model.get(), info.model.Length());
1184 if (!strModel) {
1185 return false;
1187 JS::Rooted<JS::Value> valModel(aCx, JS::StringValue(strModel));
1188 if (!JS_SetProperty(aCx, jsInfo, "model", valModel)) {
1189 return false;
1192 JSString* strRevision =
1193 JS_NewStringCopyN(aCx, info.revision.get(), info.revision.Length());
1194 if (!strRevision) {
1195 return false;
1197 JS::Rooted<JS::Value> valRevision(aCx, JS::StringValue(strRevision));
1198 if (!JS_SetProperty(aCx, jsInfo, "revision", valRevision)) {
1199 return false;
1202 JSString* strSSD = JS_NewStringCopyZ(aCx, info.isSSD ? "SSD" : "HDD");
1203 if (!strSSD) {
1204 return false;
1206 JS::Rooted<JS::Value> valSSD(aCx, JS::StringValue(strSSD));
1207 if (!JS_SetProperty(aCx, jsInfo, "type", valSSD)) {
1208 return false;
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);
1228 return jsInfo;
1231 #endif
1233 JSObject* GetJSObjForProcessInfo(JSContext* aCx, const ProcessInfo& info) {
1234 JS::Rooted<JSObject*> jsInfo(aCx, JS_NewPlainObject(aCx));
1236 #if defined(XP_WIN)
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);
1242 #endif
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);
1273 return jsInfo;
1276 RefPtr<nsISerialEventTarget> nsSystemInfo::GetBackgroundTarget() {
1277 if (!mBackgroundET) {
1278 MOZ_ALWAYS_SUCCEEDS(NS_CreateBackgroundTaskQueue(
1279 "SystemInfoThread", getter_AddRefs(mBackgroundET)));
1281 return mBackgroundET;
1284 NS_IMETHODIMP
1285 nsSystemInfo::GetOsInfo(JSContext* aCx, Promise** aResult) {
1286 NS_ENSURE_ARG_POINTER(aResult);
1287 *aResult = nullptr;
1288 if (!XRE_IsParentProcess()) {
1289 return NS_ERROR_FAILURE;
1291 #if defined(XP_WIN)
1292 nsIGlobalObject* global = xpc::CurrentNativeGlobal(aCx);
1293 if (NS_WARN_IF(!global)) {
1294 return NS_ERROR_FAILURE;
1297 ErrorResult erv;
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__, []() {
1307 OSInfo info;
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) {
1321 AutoJSAPI jsapi;
1322 if (NS_WARN_IF(!jsapi.Init(capturedPromise->GetGlobalObject()))) {
1323 capturedPromise->MaybeReject(NS_ERROR_UNEXPECTED);
1324 return;
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);
1337 #endif
1338 return NS_OK;
1341 NS_IMETHODIMP
1342 nsSystemInfo::GetDiskInfo(JSContext* aCx, Promise** aResult) {
1343 NS_ENSURE_ARG_POINTER(aResult);
1344 *aResult = nullptr;
1345 if (!XRE_IsParentProcess()) {
1346 return NS_ERROR_FAILURE;
1348 #ifdef XP_WIN
1349 nsIGlobalObject* global = xpc::CurrentNativeGlobal(aCx);
1350 if (NS_WARN_IF(!global)) {
1351 return NS_ERROR_FAILURE;
1353 ErrorResult erv;
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)) {
1366 return rv;
1368 rv = NS_GetSpecialDirectory(NS_WIN_WINDOWS_DIR, getter_AddRefs(winDir));
1369 if (NS_FAILED(rv)) {
1370 return rv;
1372 rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR,
1373 getter_AddRefs(profDir));
1374 if (NS_FAILED(rv)) {
1375 return rv;
1378 mDiskInfoPromise =
1379 InvokeAsync(backgroundET, __func__, [greDir, winDir, profDir]() {
1380 DiskInfo info;
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) {
1394 AutoJSAPI jsapi;
1395 if (NS_WARN_IF(!jsapi.Init(capturedPromise->GetGlobalObject()))) {
1396 capturedPromise->MaybeReject(NS_ERROR_UNEXPECTED);
1397 return;
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);
1410 return;
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);
1421 #endif
1422 return NS_OK;
1425 NS_IMPL_ISUPPORTS_INHERITED(nsSystemInfo, nsHashPropertyBag, nsISystemInfo)
1427 NS_IMETHODIMP
1428 nsSystemInfo::GetCountryCode(JSContext* aCx, Promise** aResult) {
1429 NS_ENSURE_ARG_POINTER(aResult);
1430 *aResult = nullptr;
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;
1441 ErrorResult erv;
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;
1452 # ifdef XP_MACOSX
1453 nsresult rv = GetSelectedCityInfo(countryCode);
1454 # endif
1455 # ifdef XP_WIN
1456 nsresult rv = CollectCountryCode(countryCode);
1457 # endif
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) {
1470 AutoJSAPI jsapi;
1471 if (NS_WARN_IF(!jsapi.Init(capturedPromise->GetGlobalObject()))) {
1472 capturedPromise->MaybeReject(NS_ERROR_UNEXPECTED);
1473 return;
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);
1488 #endif
1489 return NS_OK;
1492 NS_IMETHODIMP
1493 nsSystemInfo::GetProcessInfo(JSContext* aCx, Promise** aResult) {
1494 NS_ENSURE_ARG_POINTER(aResult);
1495 *aResult = nullptr;
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;
1506 ErrorResult erv;
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__, []() {
1516 ProcessInfo info;
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) {
1530 AutoJSAPI jsapi;
1531 if (NS_WARN_IF(!jsapi.Init(capturedPromise->GetGlobalObject()))) {
1532 capturedPromise->MaybeReject(NS_ERROR_UNEXPECTED);
1533 return;
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);
1547 return NS_OK;