- : 63 %
- : 63 %
- : 63 %
- : 63 %
- : 63 %
- : 63 %
- : 63 %
- : 63 %
- : 63 %
- : 63 %
- : 63 %
- : 63 %
- : 63 %
- : 63 %
- : 63 %
- : 63 %
- : 63 %
- : 62 %
- : 63 %
- : 63 %
- : 63 %
- : 63 %
- : 62 %
- : 63 %
- : 63 %
- : 63 %
- : 63 %
- : 63 %
- : 63 %
- : 63 %
- : 63 %
- : 63 %
- : 63 %
- : 63 %
- : 63 %
- : 63 %
- : 63 %
- : 63 %
- : 63 %
- : 63 %
- : 63 %
- : 63 %
- : 63 %
- : 63 %
- : 63 %
- : 63 %
- : 63 %
- : 63 %
- : 63 %
- : 63 %
- : 63 %
- : 63 %
- : 63 %
- : 63 %
- : 63 %
- : 62 %
- : 63 %
- : 63 %
- : 63 %
- : 63 %
- : 63 %
- : 63 %
- : 62 %
- : 63 %
- : 63 %
- : 63 %
- : 63 %
- : 63 %
- : 63 %
- : 63 %
- : 63 %
- : 62 %
- : 62 %
- : 62 %
- : 63 %
- : 63 %
- : 63 %
- : 63 %
- : 63 %
- : 62 %
- : 63 %
- : 62 %
- : 63 %
- : 63 %
- : 62 %
- : 63 %
- : 63 %
- : 63 %
- : 63 %
- : 63 %
- : 63 %
- : 63 %
- : 62 %
- : 62 %
- : 62 %
- : 63 %
- : 63 %
- : 63 %
- : 63 %
- : 63 %
- : 63 %
- : 63 %
- : 62 %
- : 60 %
- : 63 %
- : 63 %
- : 62 %
- : 62 %
- : 63 %
- : 63 %
- : 63 %
- : 63 %
- : 63 %
- : 63 %
- : 63 %
- : 63 %
- : 62 %
- : 63 %
- : 63 %
- : 62 %
- : 63 %
- : 62 %
- : 63 %
- : 62 %
- : 63 %
- : 63 %
- : 63 %
- : 63 %
- : 63 %
- : 62 %
- : 63 %
- : 63 %
- : 63 %
- : 62 %
- : 63 %
- : 63 %
- : 63 %
- : 63 %
- : 63 %
- : 63 %
- : 62 %
- : 63 %
- : 63 %
- : 63 %
- : 63 %
- : 63 %
- : 62 %
- : 63 %
- : 63 %
- : 63 %
- : 62 %
- : 63 %
- : 63 %
- : 60 %
- : 62 %
- : 62 %
- : 63 %
- : 63 %
- : 63 %
- : 62 %
- : 63 %
- : 63 %
- : 63 %
- : 63 %
- : 63 %
- : 62 %
- : 63 %
- : 63 %
- : 63 %
- : 63 %
- : 63 %
- : 63 %
- : 62 %
- : 63 %
- : 62 %
- : 63 %
- : 62 %
- : 63 %
- : 63 %
- : 63 %
- : 63 %
- : 63 %
- : 63 %
- : 63 %
- : 63 %
- : 63 %
- : 62 %
- : 63 %
- : 63 %
- : 63 %
- : 63 %
- : 63 %
- : 62 %
- : 63 %
- : 62 %
- : 63 %
- : 63 %
- : 63 %
- : 63 %
- : 63 %
- : 63 %
- : 63 %
- : 63 %
- : 63 %
- : 63 %
- : 63 %
- : 63 %
- : 63 %
- : 63 %
- : 63 %
- : 63 %
- : 62 %
- : 63 %
- : 63 %
- : 63 %
- : 63 %
- : 63 %
- : 62 %
- : 63 %
- : 63 %
- : 62 %
- : 63 %
- : 63 %
- : 63 %
- : 63 %
- : 63 %
- : 63 %
- : 62 %
- : 63 %
- : 63 %
- : 63 %
- : 63 %
- : 63 %
- : 62 %
- : 63 %
- : 62 %
- : 63 %
- : 63 %
- : 63 %
- : 63 %
- : 62 %
- : 63 %
- : 63 %
- : 63 %
- : 63 %
- : 63 %
- : 63 %
- : 63 %
- : 63 %
- : 62 %
- : 63 %
- : 63 %
- : 63 %
- : 63 %
- : 63 %
- : 63 %
- : 63 %
- : 63 %
- : 63 %
- : 63 %
- : 63 %
- : 63 %
- : 62 %
- : 63 %
- : 63 %
- : 62 %
- : 63 %
- : 63 %
- : 63 %
- : 62 %
- : 62 %
- : 60 %
- : 63 %
- : 63 %
- : 62 %
- : 63 %
- : 63 %
- : 62 %
- : 62 %
- : 63 %
- : 62 %
- : 62 %
- : 63 %
- : 63 %
- : 62 %
- : 62 %
- : 63 %
- : 59 %
- : 62 %
- : 63 %
- : 63 %
- : 62 %
- : 63 %
- : 63 %
- : 60 %
- : 62 %
- : 63 %
- : 62 %
- : 60 %
- : 62 %
- : 63 %
- : 62 %
- : 63 %
- : 62 %
- : 62 %
- : 62 %
- : 60 %
- : 63 %
- : 62 %
- : 63 %
- : 62 %
- : 63 %
- : 62 %
- : 63 %
- : 62 %
- : 62 %
- : 62 %
- : 63 %
- : 74 %
- : 72 %
- : 76 %
- : 76 %
- : 76 %
- : 72 %
- : 75 %
- : 75 %
- : 75 %
- : 75 %
- : 76 %
- : 75 %
- : 76 %
- : 75 %
- : 76 %
- : 75 %
- : 72 %
- : 76 %
- : 75 %
- : 72 %
- : 76 %
- : 75 %
- : 75 %
- : 59 %
- : 76 %
- : 75 %
- : 75 %
- : 76 %
- : 76 %
- : 75 %
- : 73 %
- : 73 %
- : 76 %
- : 75 %
- : 73 %
- : 73 %
- : 73 %
- : 76 %
- : 76 %
- : 73 %
- : 72 %
- : 73 %
- : 76 %
- : 73 %
- : 73 %
- : 73 %
- : 76 %
- : 73 %
- : 72 %
- : 75 %
- : 76 %
- : 75 %
- : 75 %
- : 75 %
- : 71 %
- : 71 %
- : 72 %
- : 72 %
- : 71 %
- : 72 %
- : 76 %
- : 72 %
- : 71 %
- : 71 %
- : 72 %
- : 71 %
- : 71 %
- : 72 %
- : 71 %
- : 72 %
- : 72 %
- : 72 %
- : 72 %
- : 71 %
- : 71 %
- : 71 %
- : 71 %
- : 71 %
- : 71 %
- : 72 %
- : 72 %
- : 72 %
- : 72 %
- : 72 %
- : 72 %
- : 72 %
- : 72 %
- : 72 %
- : 72 %
- : 72 %
- : 71 %
- : 72 %
- : 63 %
- : 63 %
- : 63 %
- : 63 %
- : 63 %
- : 64 %
- : 62 %
- : 63 %
- : 63 %
- : 64 %
- : 63 %
- : 63 %
- : 63 %
- : 63 %
- : 63 %
- : 62 %
- : 62 %
- : 63 %
- : 63 %
- : 63 %
- : 63 %
- : 62 %
- : 63 %
- : 62 %
- : 63 %
- : 62 %
- : 63 %
- : 63 %
- : 63 %
- : 63 %
- : 63 %
- : 62 %
- : 62 %
- : 63 %
- : 63 %
- : 63 %
- : 62 %
- : 62 %
- : 62 %
- : 62 %
- : 62 %
- : 62 %
- : 62 %
- : 62 %
- : 63 %
- : 62 %
- : 62 %
- : 62 %
- : 62 %
- : 62 %
- : 62 %
- : 62 %
- : 62 %
- : 63 %
- : 62 %
- : 62 %
- : 63 %
- : 62 %
- : 62 %
- : 62 %
- : 62 %
- : 62 %
- : 62 %
- : 62 %
- : 62 %
- : 62 %
- : 62 %
- : 62 %
- : 62 %
- : 62 %
- : 62 %
- : 62 %
- : 56 %
- : 62 %
- : 62 %
- : 62 %
- : 62 %
- : 62 %
- : 62 %
- : 62 %
- : 62 %
- : 62 %
- : 62 %
- : 62 %
- : 62 %
- : 62 %
- : 62 %
- : 62 %
- : 62 %
- : 62 %
- : 62 %
- : 62 %
- : 62 %
- : 62 %
- : 62 %
- : 62 %
- : 62 %
- : 62 %
- : 62 %
- : 62 %
- : 62 %
- : 62 %
- : 62 %
- : 62 %
- : 62 %
- : 62 %
- : 62 %
- : 62 %
- : 62 %
- : 62 %
- : 62 %
- : 62 %
- : 62 %
- : 62 %
- : 62 %
- : 62 %
- : 62 %
- : 62 %
- : 62 %
- : 62 %
- : 62 %
- : 62 %
- : 62 %
- : 62 %
- : 62 %
- : 62 %
- : 62 %
- : 62 %
- : 62 %
- : 62 %
- : 62 %
- : 62 %
- : 62 %
- : 62 %
- : 62 %
- : 62 %
- : 62 %
- : 62 %
- : 62 %
- : 62 %
- : 62 %
- : 62 %
- : 62 %
- : 62 %
- : 62 %
- : 62 %
- : 62 %
- : 62 %
- : 62 %
- : 62 %
- : 62 %
- : 61 %
- : 62 %
- : 62 %
- : 62 %
- : 62 %
- : 62 %
- : 62 %
- : 62 %
- : 62 %
- : 62 %
- : 62 %
- : 62 %
- : 62 %
- : 62 %
- : 62 %
- : 62 %
- : 62 %
- : 62 %
- : 62 %
- : 62 %
- : 62 %
- : 62 %
- : 62 %
- : 62 %
- : 62 %
- : 62 %
- : 62 %
- : 62 %
- : 62 %
- : 62 %
- : 62 %
- : 62 %
- : 62 %
- : 62 %
- : 62 %
- : 62 %
- : 62 %
- : 62 %
- : 62 %
- : 62 %
- : 62 %
- : 62 %
- : 62 %
- : 62 %
- : 62 %
- : 62 %
- : 62 %
- : 62 %
- : 55 %
- : 62 %
- : 62 %
- : 62 %
- : 62 %
- : 62 %
- : 62 %
- : 62 %
- : 62 %
- : 62 %
- : 62 %
- : 62 %
- : 62 %
- : 62 %
- : 62 %
- : 62 %
- : 61 %
- : 62 %
- : 62 %
- : 62 %
- : 62 %
- : 62 %
- : 62 %
- : 61 %
- : 61 %
- : 61 %
- : 65 %
- : 65 %
- : 64 %
- : 65 %
- : 65 %
- : 65 %
- : 65 %
- : 65 %
- : 65 %
- : 65 %
- : 65 %
- : 65 %
- : 65 %
- : 65 %
- : 64 %
- : 65 %
- : 65 %
- : 65 %
- : 65 %
- : 65 %
- : 65 %
- : 64 %
- : 65 %
- : 47 %
- : 45 %
- : 48 %
- : 47 %
- : 45 %
- : 49 %
- : 45 %
- : 49 %
- : 45 %
- : 65 %
- : 65 %
- : 44 %
- : 65 %
- : 65 %
- : 65 %
- : 65 %
- : 65 %
- : 65 %
- : 65 %
- : 65 %
- : 65 %
- : 65 %
- : 65 %
- : 65 %
- : 65 %
- : 65 %
- : 65 %
- : 60 %
- : 65 %
- : 65 %
- : 65 %
- : 65 %
- : 65 %
- : 65 %
- : 65 %
- : 65 %
- : 65 %
- : 65 %
- : 65 %
- : 65 %
- : 65 %
- : 65 %
- : 65 %
- : 65 %
- : 65 %
- : 65 %
- : 64 %
- : 65 %
- : 65 %
- : 65 %
- : 65 %
- : 65 %
- : 65 %
- : 65 %
- : 65 %
- : 65 %
- : 65 %
- : 65 %
- : 65 %
- : 65 %
- : 65 %
- : 65 %
- : 65 %
- : 65 %
- : 65 %
- : 65 %
- : 65 %
- : 65 %
- : 65 %
- : 65 %
- : 65 %
- : 65 %
Source code
Revision control
Copy as Markdown
Other Tools
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
#include "mozilla/dom/ContentParent.h"
#include "mozilla/dom/ContentChild.h"
#include "mozilla/ipc/GeckoChildProcessHost.h"
#include "mozilla/AppShutdown.h"
#include "mozilla/Assertions.h"
#include "mozilla/Attributes.h"
#include "mozilla/BaseProfiler.h"
#include "mozilla/Components.h"
#include "mozilla/FilePreferences.h"
#include "mozilla/FOG.h"
#include "mozilla/ChaosMode.h"
#include "mozilla/HelperMacros.h"
#include "mozilla/CmdLineAndEnvUtils.h"
#include "mozilla/IOInterposer.h"
#include "mozilla/ipc/UtilityProcessChild.h"
#include "mozilla/Likely.h"
#include "mozilla/Logging.h"
#include "mozilla/Preferences.h"
#include "mozilla/PreferenceSheet.h"
#include "mozilla/Printf.h"
#include "mozilla/ProcessType.h"
#include "mozilla/ResultExtensions.h"
#include "mozilla/RuntimeExceptionModule.h"
#include "mozilla/FileUtils.h"
#include "mozilla/ScopeExit.h"
#include "mozilla/StaticPrefs_browser.h"
#include "mozilla/StaticPrefs_fission.h"
#include "mozilla/StaticPrefs_security.h"
#include "mozilla/StaticPrefs_widget.h"
#include "mozilla/glean/SecuritySandboxMetrics.h"
#include "mozilla/Telemetry.h"
#include "mozilla/Try.h"
#include "mozilla/intl/LocaleService.h"
#include "mozilla/JSONWriter.h"
#include "mozilla/gfx/gfxVars.h"
#include "mozilla/glean/ToolkitMozappsUpdateSharedMetrics.h"
#include "mozilla/glean/ToolkitXreMetrics.h"
#include "mozilla/glean/GleanPings.h"
#include "mozilla/widget/TextRecognition.h"
#include "mozJSModuleLoader.h"
#include "nsAppRunner.h"
#include "mozilla/XREAppData.h"
#include "mozilla/Bootstrap.h"
#if defined(MOZ_UPDATER) && !defined(MOZ_WIDGET_ANDROID)
# include "nsUpdateDriver.h"
# include "nsUpdateSyncManager.h"
#endif
#include "ProfileReset.h"
#ifdef XP_MACOSX
# include "nsVersionComparator.h"
# include "nsCocoaFeatures.h"
# include "mozilla/glean/WidgetCocoaMetrics.h"
# include "MacLaunchHelper.h"
# include "MacApplicationDelegate.h"
# include "MacAutoreleasePool.h"
# include "MacRunFromDmgUtils.h"
# include "ASWebAuthSessionHandler.h"
// these are needed for sysctl
# include <sys/types.h>
# include <sys/sysctl.h>
#endif
#include "prnetdb.h"
#include "prprf.h"
#include "prproces.h"
#include "prenv.h"
#include "prtime.h"
#include "nsIAppStartup.h"
#include "nsCategoryManagerUtils.h"
#include "nsIMutableArray.h"
#include "nsCommandLine.h"
#include "nsIComponentRegistrar.h"
#include "nsIDialogParamBlock.h"
#include "mozilla/ModuleUtils.h"
#include "nsIIOService.h"
#include "nsIObserverService.h"
#include "nsINativeAppSupport.h"
#include "nsIPlatformInfo.h"
#include "nsIProcess.h"
#include "nsIProfileUnlocker.h"
#include "nsIPromptService.h"
#include "nsIPropertyBag2.h"
#include "nsIServiceManager.h"
#include "nsIStringBundle.h"
#include "nsISupportsPrimitives.h"
#include "nsIToolkitProfile.h"
#include "nsToolkitProfileService.h"
#include "nsIURI.h"
#include "nsIURL.h"
#include "nsIWindowCreator.h"
#include "nsIWindowWatcher.h"
#include "nsIXULAppInfo.h"
#include "nsIXULRuntime.h"
#include "nsPIDOMWindow.h"
#include "nsIWidget.h"
#include "nsAppShellCID.h"
#include "mozilla/dom/quota/QuotaManager.h"
#include "mozilla/scache/StartupCache.h"
#include "gfxPlatform.h"
#include "PDMFactory.h"
#ifdef XP_MACOSX
# include "gfxPlatformMac.h"
#endif
#ifdef XP_WIN
# include "nsIWinAppHelper.h"
# include <windows.h>
# include <intrin.h>
# include <math.h>
# include "cairo/cairo-features.h"
# include "mozilla/PreXULSkeletonUI.h"
# include "mozilla/DllPrefetchExperimentRegistryInfo.h"
# include "mozilla/WindowsDllBlocklist.h"
# include "mozilla/WindowsMsctfInitialization.h"
# include "mozilla/WindowsOleAut32Initialization.h"
# include "mozilla/WindowsProcessMitigations.h"
# include "mozilla/WindowsVersion.h"
# include "mozilla/WinHeaderOnlyUtils.h"
# include "mozilla/mscom/ProcessRuntime.h"
# include "mozilla/mscom/ProfilerMarkers.h"
# include "WinTokenUtils.h"
# if defined(MOZ_LAUNCHER_PROCESS)
# include "mozilla/LauncherRegistryInfo.h"
# endif
# if defined(MOZ_DEFAULT_BROWSER_AGENT)
# include "nsIWindowsRegKey.h"
# endif
# ifndef PROCESS_DEP_ENABLE
# define PROCESS_DEP_ENABLE 0x1
# endif
#endif
#if defined(MOZ_SANDBOX)
# include "mozilla/SandboxSettings.h"
#endif
#ifdef ACCESSIBILITY
# include "nsAccessibilityService.h"
# include "mozilla/a11y/Platform.h"
# if defined(XP_WIN)
# include "mozilla/a11y/Compatibility.h"
# endif
#endif
#include "json/json.h"
#include "nsCRT.h"
#include "nsCOMPtr.h"
#include "nsDirectoryServiceDefs.h"
#include "nsDirectoryServiceUtils.h"
#include "nsEmbedCID.h"
#include "nsIDUtils.h"
#include "nsNetUtil.h"
#include "nsReadableUtils.h"
#include "nsXPCOM.h"
#include "nsXPCOMCIDInternal.h"
#include "nsString.h"
#include "nsPrintfCString.h"
#include "nsVersionComparator.h"
#include "nsAppDirectoryServiceDefs.h"
#include "nsXULAppAPI.h"
#include "nsXREDirProvider.h"
#include "nsINIParser.h"
#include "mozilla/Omnijar.h"
#include "mozilla/StartupTimeline.h"
#include "mozilla/LateWriteChecks.h"
#include <stdlib.h>
#include <string_view>
#ifdef XP_UNIX
# include <errno.h>
# include <pwd.h>
# include <string.h>
# include <sys/resource.h>
# include <sys/stat.h>
# include <unistd.h>
#endif
#ifdef XP_WIN
# include <process.h>
# include <shlobj.h>
# include "mozilla/WinDllServices.h"
# include "nsThreadUtils.h"
# include "WinUtils.h"
#endif
#ifdef XP_MACOSX
# include "nsILocalFileMac.h"
# include "nsCommandLineServiceMac.h"
#endif
// for X remote support
#if defined(MOZ_HAS_REMOTE)
# include "nsRemoteService.h"
#endif
#if defined(DEBUG) && defined(XP_WIN)
# include <malloc.h>
#endif
#if defined(XP_MACOSX)
# include <Carbon/Carbon.h>
#endif
#ifdef DEBUG
# include "mozilla/Logging.h"
# include "mozilla/StaticPrefs_toolkit.h"
#endif
#include "nsExceptionHandler.h"
#include "nsICrashReporter.h"
#include "nsIPrefService.h"
#include "nsIMemoryInfoDumper.h"
#include "js/String.h"
#if defined(XP_LINUX) && !defined(ANDROID)
# include "mozilla/widget/LSBUtils.h"
#endif
#include "base/command_line.h"
#include "GTestRunner.h"
#ifdef MOZ_WIDGET_ANDROID
# include "gfxAndroidPlatform.h"
# include "mozilla/java/GeckoAppShellWrappers.h"
#endif
#if defined(MOZ_SANDBOX)
# if defined(XP_LINUX) && !defined(ANDROID)
# include "mozilla/SandboxInfo.h"
# elif defined(XP_WIN)
# include "sandboxBroker.h"
# endif
#endif
#ifdef MOZ_CODE_COVERAGE
# include "mozilla/CodeCoverageHandler.h"
#endif
#include "GMPProcessChild.h"
#include "SafeMode.h"
#ifdef MOZ_BACKGROUNDTASKS
# include "mozilla/BackgroundTasks.h"
# include "nsIPowerManagerService.h"
# include "nsIStringBundle.h"
#endif
#ifdef USE_GLX_TEST
# include "mozilla/GUniquePtr.h"
# include "mozilla/GfxInfo.h"
#endif
#ifdef MOZ_WIDGET_GTK
# include "nsAppShell.h"
#endif
#ifdef MOZ_ENABLE_DBUS
# include "DBusService.h"
#endif
extern void InstallSignalHandlers(const char* ProgramName);
#define FILE_COMPATIBILITY_INFO "compatibility.ini"_ns
#define FILE_INVALIDATE_CACHES ".purgecaches"_ns
#define FILE_STARTUP_INCOMPLETE u".startup-incomplete"_ns
#if defined(MOZ_BLOCK_PROFILE_DOWNGRADE) || defined(MOZ_LAUNCHER_PROCESS) || \
defined(MOZ_DEFAULT_BROWSER_AGENT)
static const char kPrefHealthReportUploadEnabled[] =
"datareporting.healthreport.uploadEnabled";
#endif // defined(MOZ_BLOCK_PROFILE_DOWNGRADE) || defined(MOZ_LAUNCHER_PROCESS)
// || defined(MOZ_DEFAULT_BROWSER_AGENT)
#if defined(MOZ_DEFAULT_BROWSER_AGENT)
static const char kPrefDefaultAgentEnabled[] = "default-browser-agent.enabled";
static const char kPrefServicesSettingsServer[] = "services.settings.server";
static const char kPrefSetDefaultBrowserUserChoicePref[] =
"browser.shell.setDefaultBrowserUserChoice";
#endif // defined(MOZ_DEFAULT_BROWSER_AGENT)
#if defined(XP_WIN)
static const char kPrefThemeId[] = "extensions.activeThemeID";
# if defined(MOZ_DEFAULT_BROWSER_AGENT)
static const char kPrefBrowserStartupBlankWindow[] =
"browser.startup.blankWindow";
static const char kPrefPreXulSkeletonUI[] = "browser.startup.preXulSkeletonUI";
# endif // defined(MOZ_DEFAULT_BROWSER_AGENT)
#endif // defined(XP_WIN)
#if defined(MOZ_WIDGET_GTK)
constexpr nsLiteralCString kStartupTokenNames[] = {
"XDG_ACTIVATION_TOKEN"_ns,
"DESKTOP_STARTUP_ID"_ns,
};
#endif
int gArgc;
char** gArgv;
static const char gToolkitVersion[] = MOZ_STRINGIFY(GRE_MILESTONE);
// The gToolkitBuildID global is defined to MOZ_BUILDID via gen_buildid.py
// in toolkit/library. See related comment in toolkit/library/moz.build.
extern const char gToolkitBuildID[];
static nsIProfileLock* gProfileLock;
#if defined(MOZ_HAS_REMOTE)
constinit static RefPtr<nsRemoteService> gRemoteService;
constinit static RefPtr<nsStartupLock> gStartupLock;
#endif
int gRestartArgc;
char** gRestartArgv;
// If gRestartedByOS is set, we were automatically restarted by the OS.
bool gRestartedByOS = false;
bool gIsGtest = false;
bool gKioskMode = false;
int gKioskMonitor = -1;
bool gAllowContentAnalysisArgPresent = false;
constinit nsString gAbsoluteArgv0Path;
#if defined(XP_WIN)
constinit nsString gProcessStartupShortcut;
#endif
#if defined(MOZ_WIDGET_GTK)
# include <glib.h>
# include "mozilla/WidgetUtilsGtk.h"
# include <gtk/gtk.h>
# ifdef MOZ_WAYLAND
# include <gdk/gdkwayland.h>
# include "mozilla/widget/nsWaylandDisplay.h"
# include "wayland-proxy.h"
# endif
# ifdef MOZ_X11
# include <gdk/gdkx.h>
# endif /* MOZ_X11 */
#endif
#if defined(MOZ_WAYLAND)
constinit std::unique_ptr<WaylandProxy> gWaylandProxy;
#endif
#include "BinaryPath.h"
#ifdef FUZZING
# include "FuzzerRunner.h"
namespace mozilla {
FuzzerRunner* fuzzerRunner = 0;
} // namespace mozilla
# ifdef LIBFUZZER
void XRE_LibFuzzerSetDriver(LibFuzzerDriver aDriver) {
mozilla::fuzzerRunner->setParams(aDriver);
}
# endif
#endif // FUZZING
// Undo X11/X.h's definition of None
#undef None
namespace mozilla {
int (*RunGTest)(int*, char**) = nullptr;
bool RunningGTest() { return RunGTest; }
} // namespace mozilla
using namespace mozilla;
using namespace mozilla::widget;
using namespace mozilla::startup;
using mozilla::dom::ContentChild;
using mozilla::dom::ContentParent;
using mozilla::dom::quota::QuotaManager;
using mozilla::intl::LocaleService;
using mozilla::scache::StartupCache;
#ifndef XP_WIN
// Save the given word to the specified environment variable.
static void MOZ_NEVER_INLINE SaveWordToEnv(const char* name,
const nsACString& word) {
char* expr =
Smprintf("%s=%s", name, PromiseFlatCString(word).get()).release();
if (expr) PR_SetEnv(expr);
// We intentionally leak |expr| here since it is required by PR_SetEnv.
}
#endif
// Save the path of the given file to the specified environment variable.
static void SaveFileToEnv(const char* name, nsIFile* file) {
#ifdef XP_WIN
nsAutoString path;
file->GetPath(path);
SetEnvironmentVariableW(NS_ConvertASCIItoUTF16(name).get(), path.get());
#else
nsAutoCString path;
file->GetNativePath(path);
SaveWordToEnv(name, path);
#endif
}
static bool gIsExpectedExit = false;
void MozExpectedExit() { gIsExpectedExit = true; }
/**
* Runs atexit() to catch unexpected exit from 3rd party libraries like the
* Intel graphics driver calling exit in an error condition. When they
* call exit() to report an error we won't shutdown correctly and wont catch
* the issue with our crash reporter.
*/
static void UnexpectedExit() {
if (!gIsExpectedExit) {
gIsExpectedExit = true; // Don't risk re-entrency issues when crashing.
MOZ_CRASH("Exit called by third party code.");
}
}
#if defined(MOZ_WAYLAND)
bool IsWaylandEnabled() {
static bool isWaylandEnabled = []() {
const char* waylandDisplay = PR_GetEnv("WAYLAND_DISPLAY");
if (!waylandDisplay) {
return false;
}
if (!PR_GetEnv("DISPLAY")) {
// No X11 display, so try to run wayland.
return true;
}
// MOZ_ENABLE_WAYLAND is our primary Wayland on/off switch.
if (const char* waylandPref = PR_GetEnv("MOZ_ENABLE_WAYLAND")) {
return *waylandPref == '1';
}
if (const char* backendPref = PR_GetEnv("GDK_BACKEND")) {
if (!strncmp(backendPref, "wayland", 7)) {
NS_WARNING(
"Wayland backend should be enabled by MOZ_ENABLE_WAYLAND=1."
"GDK_BACKEND is a Gtk3 debug variable and may cause issues.");
return true;
}
}
// Enable by default when we're running on a recent enough GTK version. We'd
// like to check further details like compositor version and so on ideally
// to make sure we don't enable it on old Mutter or what not, but we can't,
// so let's assume that if the user is running on a Wayland session by
// default we're ok, since either the distro has enabled Wayland by default,
// or the user has gone out of their way to use Wayland.
return !gtk_check_version(3, 24, 30);
}();
return isWaylandEnabled;
}
#else
bool IsWaylandEnabled() { return false; }
#endif
/**
* Output a string to the user. This method is really only meant to be used to
* output last-ditch error messages designed for developers NOT END USERS.
*
* @param isError
* Pass true to indicate severe errors.
* @param fmt
* printf-style format string followed by arguments.
*/
static MOZ_FORMAT_PRINTF(2, 3) void Output(bool isError, const char* fmt, ...) {
va_list ap;
va_start(ap, fmt);
#if defined(XP_WIN) && !MOZ_WINCONSOLE
SmprintfPointer msg = mozilla::Vsmprintf(fmt, ap);
if (msg) {
UINT flags = MB_OK;
if (isError)
flags |= MB_ICONERROR;
else
flags |= MB_ICONINFORMATION;
wchar_t wide_msg[1024];
MultiByteToWideChar(CP_ACP, 0, msg.get(), -1, wide_msg,
sizeof(wide_msg) / sizeof(wchar_t));
wchar_t wide_caption[128];
MultiByteToWideChar(CP_ACP, 0, gAppData ? gAppData->name : "XULRunner", -1,
wide_caption, sizeof(wide_caption) / sizeof(wchar_t));
MessageBoxW(nullptr, wide_msg, wide_caption, flags);
}
#elif defined(MOZ_WIDGET_ANDROID)
SmprintfPointer msg = mozilla::Vsmprintf(fmt, ap);
if (msg) {
__android_log_print(isError ? ANDROID_LOG_ERROR : ANDROID_LOG_INFO,
"GeckoRuntime", "%s", msg.get());
}
#else
vfprintf(stderr, fmt, ap);
#endif
va_end(ap);
}
/**
* Check for a commandline flag. If the flag takes a parameter, the
* parameter is returned in aParam. Flags may be in the form -arg or
* --arg (or /arg on win32).
*
* @param aArg the parameter to check. Must be lowercase.
* @param aParam if non-null, the -arg <data> will be stored in this pointer.
* This is *not* allocated, but rather a pointer to the argv data.
* @param aFlags flags @see CheckArgFlag
*/
static ArgResult CheckArg(const char* aArg, const char** aParam = nullptr,
CheckArgFlag aFlags = CheckArgFlag::RemoveArg) {
MOZ_ASSERT(gArgv, "gArgv must be initialized before CheckArg()");
return CheckArg(gArgc, gArgv, aArg, aParam, aFlags);
}
/**
* Check for a commandline flag. Ignore data that's passed in with the flag.
* Flags may be in the form -arg or --arg (or /arg on win32).
* Will not remove flag if found.
*
* @param aArg the parameter to check. Must be lowercase.
*/
static ArgResult CheckArgExists(const char* aArg) {
return CheckArg(aArg, nullptr, CheckArgFlag::None);
}
bool gSafeMode = false;
bool gFxREmbedded = false;
enum E10sStatus {
kE10sEnabledByDefault,
kE10sForceDisabled,
};
static bool gBrowserTabsRemoteAutostart = false;
static E10sStatus gBrowserTabsRemoteStatus;
static bool gBrowserTabsRemoteAutostartInitialized = false;
namespace mozilla {
bool BrowserTabsRemoteAutostart() {
if (gBrowserTabsRemoteAutostartInitialized) {
return gBrowserTabsRemoteAutostart;
}
gBrowserTabsRemoteAutostartInitialized = true;
// If we're not in the parent process, we are running E10s.
if (!XRE_IsParentProcess()) {
gBrowserTabsRemoteAutostart = true;
return gBrowserTabsRemoteAutostart;
}
gBrowserTabsRemoteAutostart = true;
E10sStatus status = kE10sEnabledByDefault;
// We are checking to ensure that either MOZILLA_OFFICIAL is undefined or that
// xpc::AreNonLocalConnectionsDisabled() is true. If either check matches,
// we move on to check the MOZ_FORCE_DISABLE_E10S environment variable which
// must equal "1" to proceed with disabling e10s.
#if defined(MOZILLA_OFFICIAL)
bool allowDisablingE10s = xpc::AreNonLocalConnectionsDisabled();
#else
bool allowDisablingE10s = true;
#endif
if (gBrowserTabsRemoteAutostart && allowDisablingE10s) {
const char* forceDisable = PR_GetEnv("MOZ_FORCE_DISABLE_E10S");
if (forceDisable && !strcmp(forceDisable, "1")) {
gBrowserTabsRemoteAutostart = false;
status = kE10sForceDisabled;
}
}
gBrowserTabsRemoteStatus = status;
return gBrowserTabsRemoteAutostart;
}
} // namespace mozilla
// Win32k Infrastructure ==============================================
// This bool tells us if we have initialized the following two statics
// upon startup.
static bool gWin32kInitialized = false;
// Win32k Lockdown for the current session is determined once, at startup,
// and then remains the same for the duration of the session.
static nsIXULRuntime::ContentWin32kLockdownState gWin32kStatus;
// The status of the win32k experiment. It is determined once, at startup,
// and then remains the same for the duration of the session.
static nsIXULRuntime::ExperimentStatus gWin32kExperimentStatus =
nsIXULRuntime::eExperimentStatusUnenrolled;
#ifdef XP_WIN
// The states for win32k lockdown can be generalized to:
// - User doesn't meet requirements (bad OS, webrender, remotegl) aka
// PersistentRequirement
// - User has Safe Mode enabled, Win32k Disabled by Env Var, or E10S disabled
// by Env Var aka TemporaryRequirement
// - User has set the win32k pref to something non-default aka NonDefaultPref
// - User has been enrolled in the experiment and does what it says aka
// Enrolled
// - User does the default aka Default
//
// We expect the below behvaior. In the code, there may be references to these
// behaviors (e.g. [A]) but not always.
//
// [A] Becoming enrolled in the experiment while NonDefaultPref disqualifies
// you upon next browser start
// [B] Becoming enrolled in the experiment while PersistentRequirement
// disqualifies you upon next browser start
// [C] Becoming enrolled in the experiment while TemporaryRequirement does not
// disqualify you
// [D] Becoming enrolled in the experiment will only take effect after restart
// [E] While enrolled, becoming PersistentRequirement will not disqualify you
// until browser restart, but if the state is present at browser start,
// will disqualify you. Additionally, it will not affect the session value
// of gWin32kStatus.
// XXX(bobowen): I hope both webrender and wbgl.out-of-process require a
// restart to take effect!
// [F] While enrolled, becoming NonDefaultPref will disqualify you upon next
// browser start
// [G] While enrolled, becoming TemporaryRequirement will _not_ disqualify you,
// but the session status will prevent win32k lockdown from taking effect.
// The win32k pref
static const char kPrefWin32k[] = "security.sandbox.content.win32k-disable";
// The current enrollment status as controlled by Normandy. This value is only
// stored in the default preference branch, and is not persisted across
// sessions by the preference service. It therefore isn't available early
// enough at startup, and needs to be synced to a preference in the user
// branch which is persisted across sessions.
static const char kPrefWin32kExperimentEnrollmentStatus[] =
"security.sandbox.content.win32k-experiment.enrollmentStatus";
// The enrollment status to be used at browser startup. This automatically
// synced from the above enrollmentStatus preference whenever the latter is
// changed. We reused the Fission experiment enum - it can have any of the
// values defined in the `nsIXULRuntime_ExperimentStatus` enum _except_ rollout.
// Meanings are documented in the declaration of
// `nsIXULRuntime.fissionExperimentStatus`
static const char kPrefWin32kExperimentStartupEnrollmentStatus[] =
"security.sandbox.content.win32k-experiment.startupEnrollmentStatus";
namespace mozilla {
bool Win32kExperimentEnrolled() {
MOZ_ASSERT(XRE_IsParentProcess());
return gWin32kExperimentStatus == nsIXULRuntime::eExperimentStatusControl ||
gWin32kExperimentStatus == nsIXULRuntime::eExperimentStatusTreatment;
}
} // namespace mozilla
static bool Win32kRequirementsUnsatisfied(
nsIXULRuntime::ContentWin32kLockdownState aStatus) {
return aStatus == nsIXULRuntime::ContentWin32kLockdownState::
OperatingSystemNotSupported ||
aStatus ==
nsIXULRuntime::ContentWin32kLockdownState::MissingWebRender ||
aStatus ==
nsIXULRuntime::ContentWin32kLockdownState::DecodersArentRemote;
}
static void Win32kExperimentDisqualify() {
MOZ_ASSERT(XRE_IsParentProcess());
Preferences::SetUint(kPrefWin32kExperimentEnrollmentStatus,
nsIXULRuntime::eExperimentStatusDisqualified);
}
static void OnWin32kEnrollmentStatusChanged(const char* aPref, void* aData) {
auto newStatusInt =
Preferences::GetUint(kPrefWin32kExperimentEnrollmentStatus,
nsIXULRuntime::eExperimentStatusUnenrolled);
auto newStatus = newStatusInt < nsIXULRuntime::eExperimentStatusCount
? nsIXULRuntime::ExperimentStatus(newStatusInt)
: nsIXULRuntime::eExperimentStatusDisqualified;
// Set the startup pref for next browser start [D]
Preferences::SetUint(kPrefWin32kExperimentStartupEnrollmentStatus, newStatus);
}
namespace {
// This observer is notified during `profile-before-change`, and ensures that
// the experiment enrollment status is synced over before the browser shuts
// down, even if it was not modified since win32k was initialized.
class Win32kEnrollmentStatusShutdownObserver final : public nsIObserver {
public:
NS_DECL_ISUPPORTS
NS_IMETHOD Observe(nsISupports* aSubject, const char* aTopic,
const char16_t* aData) override {
MOZ_ASSERT(!strcmp("profile-before-change", aTopic));
OnWin32kEnrollmentStatusChanged(kPrefWin32kExperimentEnrollmentStatus,
nullptr);
return NS_OK;
}
private:
~Win32kEnrollmentStatusShutdownObserver() = default;
};
NS_IMPL_ISUPPORTS(Win32kEnrollmentStatusShutdownObserver, nsIObserver)
} // namespace
static void OnWin32kChanged(const char* aPref, void* aData) {
// If we're actively enrolled in the Win32k experiment, disqualify the user
// from the experiment if the Win32k pref is modified. [F]
if (Win32kExperimentEnrolled() && Preferences::HasUserValue(kPrefWin32k)) {
Win32kExperimentDisqualify();
}
}
#endif // XP_WIN
namespace mozilla {
void EnsureWin32kInitialized();
}
nsIXULRuntime::ContentWin32kLockdownState GetLiveWin32kLockdownState() {
#ifdef XP_WIN
// HasUserValue The Pref functions can only be called on main thread
MOZ_ASSERT(NS_IsMainThread());
# ifdef MOZ_BACKGROUNDTASKS
if (BackgroundTasks::IsBackgroundTaskMode()) {
// Let's bail out before loading all the graphics libs.
return nsIXULRuntime::ContentWin32kLockdownState::DisabledByDefault;
}
# endif
mozilla::EnsureWin32kInitialized();
gfxPlatform::GetPlatform();
if (EnvHasValue("MOZ_ENABLE_WIN32K")) {
return nsIXULRuntime::ContentWin32kLockdownState::DisabledByEnvVar;
}
if (!mozilla::BrowserTabsRemoteAutostart()) {
return nsIXULRuntime::ContentWin32kLockdownState::DisabledByE10S;
}
// Win32k lockdown is available on Win8+, but we are initially limiting it to
// Windows 10 v1709 (build 16299) or later. Before this COM initialization
// currently fails if user32.dll has loaded before it is called.
if (!IsWin10FallCreatorsUpdateOrLater()) {
return nsIXULRuntime::ContentWin32kLockdownState::
OperatingSystemNotSupported;
}
// Some (not sure exactly which) decoders are not compatible
if (!PDMFactory::AllDecodersAreRemote()) {
return nsIXULRuntime::ContentWin32kLockdownState::DecodersArentRemote;
}
bool prefSetByUser =
Preferences::HasUserValue("security.sandbox.content.win32k-disable");
bool prefValue = Preferences::GetBool(
"security.sandbox.content.win32k-disable", false, PrefValueKind::User);
bool defaultValue = Preferences::GetBool(
"security.sandbox.content.win32k-disable", false, PrefValueKind::Default);
if (prefSetByUser) {
if (prefValue) {
return nsIXULRuntime::ContentWin32kLockdownState::EnabledByUserPref;
} else {
return nsIXULRuntime::ContentWin32kLockdownState::DisabledByUserPref;
}
}
if (gWin32kExperimentStatus ==
nsIXULRuntime::ExperimentStatus::eExperimentStatusControl) {
return nsIXULRuntime::ContentWin32kLockdownState::DisabledByControlGroup;
} else if (gWin32kExperimentStatus ==
nsIXULRuntime::ExperimentStatus::eExperimentStatusTreatment) {
return nsIXULRuntime::ContentWin32kLockdownState::EnabledByTreatmentGroup;
}
if (defaultValue) {
return nsIXULRuntime::ContentWin32kLockdownState::EnabledByDefault;
} else {
return nsIXULRuntime::ContentWin32kLockdownState::DisabledByDefault;
}
#else
return nsIXULRuntime::ContentWin32kLockdownState::OperatingSystemNotSupported;
#endif
}
namespace mozilla {
void EnsureWin32kInitialized() {
if (gWin32kInitialized) {
return;
}
gWin32kInitialized = true;
#ifdef XP_WIN
// Initialize the Win32k experiment, configuring win32k's
// default, before checking other overrides. This allows opting-out of a
// Win32k experiment through about:preferences or about:config from a
// safemode session.
uint32_t experimentRaw =
Preferences::GetUint(kPrefWin32kExperimentStartupEnrollmentStatus,
nsIXULRuntime::eExperimentStatusUnenrolled);
gWin32kExperimentStatus =
experimentRaw < nsIXULRuntime::eExperimentStatusCount
? nsIXULRuntime::ExperimentStatus(experimentRaw)
: nsIXULRuntime::eExperimentStatusDisqualified;
// Watch the experiment enrollment status pref to detect experiment
// disqualification, and ensure it is propagated for the next restart.
Preferences::RegisterCallback(&OnWin32kEnrollmentStatusChanged,
kPrefWin32kExperimentEnrollmentStatus);
if (nsCOMPtr<nsIObserverService> observerService =
mozilla::services::GetObserverService()) {
nsCOMPtr<nsIObserver> shutdownObserver =
new Win32kEnrollmentStatusShutdownObserver();
observerService->AddObserver(shutdownObserver, "profile-before-change",
false);
}
// If the user no longer qualifies because they edited a required pref, check
// that. [B] [E]
auto tmpStatus = GetLiveWin32kLockdownState();
if (Win32kExperimentEnrolled() && Win32kRequirementsUnsatisfied(tmpStatus)) {
Win32kExperimentDisqualify();
gWin32kExperimentStatus = nsIXULRuntime::eExperimentStatusDisqualified;
}
// If the user has overridden an active experiment by setting a user value for
// "security.sandbox.content.win32k-disable", disqualify the user from the
// experiment. [A] [F]
if (Preferences::HasUserValue(kPrefWin32k) && Win32kExperimentEnrolled()) {
Win32kExperimentDisqualify();
gWin32kExperimentStatus = nsIXULRuntime::eExperimentStatusDisqualified;
}
// Unlike Fission, we do not configure the default branch based on experiment
// enrollment status. Instead we check the startupEnrollmentPref in
// GetContentWin32kLockdownState()
Preferences::RegisterCallback(&OnWin32kChanged, kPrefWin32k);
// Set the state
gWin32kStatus = GetLiveWin32kLockdownState();
#else
gWin32kStatus =
nsIXULRuntime::ContentWin32kLockdownState::OperatingSystemNotSupported;
gWin32kExperimentStatus = nsIXULRuntime::eExperimentStatusUnenrolled;
#endif // XP_WIN
}
nsIXULRuntime::ContentWin32kLockdownState GetWin32kLockdownState() {
#ifdef XP_WIN
mozilla::EnsureWin32kInitialized();
return gWin32kStatus;
#else
return nsIXULRuntime::ContentWin32kLockdownState::OperatingSystemNotSupported;
#endif
}
} // namespace mozilla
// End Win32k Infrastructure ==========================================
// Fission Infrastructure =============================================
// Fission enablement for the current session is determined once, at startup,
// and then remains the same for the duration of the session.
//
// The following factors determine whether or not Fission is enabled for a
// session, in order of precedence:
//
// - Safe mode: In safe mode, Fission is never enabled.
//
// - The MOZ_FORCE_ENABLE_FISSION environment variable: If set to any value,
// Fission will be enabled.
//
// - The 'fission.autostart' preference, if it has been configured by the user.
static const char kPrefFissionAutostart[] = "fission.autostart";
// The computed FissionAutostart value for the session, read by content
// processes to initialize gFissionAutostart.
//
// This pref is locked, and only configured on the default branch, so should
// never be persisted in a profile.
static const char kPrefFissionAutostartSession[] = "fission.autostart.session";
//
// The computed FissionAutostart value for the session, read by content
// processes to initialize gFissionAutostart.
static bool gFissionAutostart = false;
static bool gFissionAutostartInitialized = false;
static nsIXULRuntime::FissionDecisionStatus gFissionDecisionStatus;
static void EnsureFissionAutostartInitialized() {
if (gFissionAutostartInitialized) {
return;
}
gFissionAutostartInitialized = true;
if (!XRE_IsParentProcess()) {
// This pref is configured for the current session by the parent process.
gFissionAutostart = Preferences::GetBool(kPrefFissionAutostartSession,
false, PrefValueKind::Default);
return;
}
if (!BrowserTabsRemoteAutostart()) {
gFissionAutostart = false;
if (gBrowserTabsRemoteStatus == kE10sForceDisabled) {
gFissionDecisionStatus = nsIXULRuntime::eFissionDisabledByE10sEnv;
} else {
gFissionDecisionStatus = nsIXULRuntime::eFissionDisabledByE10sOther;
}
} else if (EnvHasValue("MOZ_FORCE_ENABLE_FISSION")) {
gFissionAutostart = true;
gFissionDecisionStatus = nsIXULRuntime::eFissionEnabledByEnv;
} else if (EnvHasValue("MOZ_FORCE_DISABLE_FISSION")) {
gFissionAutostart = false;
gFissionDecisionStatus = nsIXULRuntime::eFissionDisabledByEnv;
} else {
// NOTE: This will take into account changes to the default due to
// `InitializeFissionExperimentStatus`.
gFissionAutostart = Preferences::GetBool(kPrefFissionAutostart, false);
if (Preferences::HasUserValue(kPrefFissionAutostart)) {
gFissionDecisionStatus = gFissionAutostart
? nsIXULRuntime::eFissionEnabledByUserPref
: nsIXULRuntime::eFissionDisabledByUserPref;
} else {
gFissionDecisionStatus = gFissionAutostart
? nsIXULRuntime::eFissionEnabledByDefault
: nsIXULRuntime::eFissionDisabledByDefault;
}
}
// Content processes cannot run the same logic as we're running in the parent
// process, as the current value of various preferences may have changed
// between launches. Instead, the content process will read the default branch
// of the locked `fission.autostart.session` preference to determine the value
// determined by this method.
Preferences::Unlock(kPrefFissionAutostartSession);
Preferences::ClearUser(kPrefFissionAutostartSession);
Preferences::SetBool(kPrefFissionAutostartSession, gFissionAutostart,
PrefValueKind::Default);
Preferences::Lock(kPrefFissionAutostartSession);
}
namespace mozilla {
bool FissionAutostart() {
EnsureFissionAutostartInitialized();
return gFissionAutostart;
}
} // namespace mozilla
// End Fission Infrastructure =========================================
namespace mozilla {
bool SessionStorePlatformCollection() {
return !StaticPrefs::
browser_sessionstore_disable_platform_collection_AtStartup_DoNotUseDirectly();
}
bool BFCacheInParent() {
return StaticPrefs::fission_bfcacheInParent_DoNotUseDirectly();
}
} // namespace mozilla
/**
* The nsXULAppInfo object implements nsIFactory so that it can be its own
* singleton.
*/
class nsXULAppInfo : public nsIXULAppInfo,
#ifdef XP_WIN
public nsIWinAppHelper,
#endif
public nsICrashReporter,
public nsIFinishDumpingCallback,
public nsIXULRuntime
{
public:
constexpr nsXULAppInfo() = default;
NS_DECL_ISUPPORTS_INHERITED
NS_DECL_NSIPLATFORMINFO
NS_DECL_NSIXULAPPINFO
NS_DECL_NSIXULRUNTIME
NS_DECL_NSICRASHREPORTER
NS_DECL_NSIFINISHDUMPINGCALLBACK
#ifdef XP_WIN
NS_DECL_NSIWINAPPHELPER
#endif
};
NS_INTERFACE_MAP_BEGIN(nsXULAppInfo)
NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIXULRuntime)
NS_INTERFACE_MAP_ENTRY(nsIXULRuntime)
#ifdef XP_WIN
NS_INTERFACE_MAP_ENTRY(nsIWinAppHelper)
#endif
NS_INTERFACE_MAP_ENTRY(nsICrashReporter)
NS_INTERFACE_MAP_ENTRY(nsIFinishDumpingCallback)
NS_INTERFACE_MAP_ENTRY(nsIPlatformInfo)
NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIXULAppInfo,
gAppData || XRE_IsContentProcess())
NS_INTERFACE_MAP_END
NS_IMETHODIMP_(MozExternalRefCountType)
nsXULAppInfo::AddRef() { return 1; }
NS_IMETHODIMP_(MozExternalRefCountType)
nsXULAppInfo::Release() { return 1; }
NS_IMETHODIMP
nsXULAppInfo::GetVendor(nsACString& aResult) {
if (XRE_IsContentProcess()) {
ContentChild* cc = ContentChild::GetSingleton();
aResult = cc->GetAppInfo().vendor;
return NS_OK;
}
aResult.Assign(gAppData->vendor);
return NS_OK;
}
NS_IMETHODIMP
nsXULAppInfo::GetName(nsACString& aResult) {
if (XRE_IsContentProcess()) {
ContentChild* cc = ContentChild::GetSingleton();
aResult = cc->GetAppInfo().name;
return NS_OK;
}
#ifdef MOZ_WIDGET_ANDROID
nsCString name = java::GeckoAppShell::GetAppName()->ToCString();
aResult.Assign(std::move(name));
#else
aResult.Assign(gAppData->name);
#endif
return NS_OK;
}
NS_IMETHODIMP
nsXULAppInfo::GetID(nsACString& aResult) {
if (XRE_IsContentProcess()) {
ContentChild* cc = ContentChild::GetSingleton();
aResult = cc->GetAppInfo().ID;
return NS_OK;
}
aResult.Assign(gAppData->ID);
return NS_OK;
}
NS_IMETHODIMP
nsXULAppInfo::GetVersion(nsACString& aResult) {
if (XRE_IsContentProcess()) {
ContentChild* cc = ContentChild::GetSingleton();
aResult = cc->GetAppInfo().version;
return NS_OK;
}
aResult.Assign(gAppData->version);
return NS_OK;
}
NS_IMETHODIMP
nsXULAppInfo::GetPlatformVersion(nsACString& aResult) {
aResult.Assign(gToolkitVersion);
return NS_OK;
}
NS_IMETHODIMP
nsXULAppInfo::GetAppBuildID(nsACString& aResult) {
if (XRE_IsContentProcess()) {
ContentChild* cc = ContentChild::GetSingleton();
aResult = cc->GetAppInfo().buildID;
return NS_OK;
}
aResult.Assign(gAppData->buildID);
return NS_OK;
}
NS_IMETHODIMP
nsXULAppInfo::GetPlatformBuildID(nsACString& aResult) {
aResult.Assign(gToolkitBuildID);
return NS_OK;
}
NS_IMETHODIMP
nsXULAppInfo::GetUAName(nsACString& aResult) {
if (XRE_IsContentProcess()) {
ContentChild* cc = ContentChild::GetSingleton();
aResult = cc->GetAppInfo().UAName;
return NS_OK;
}
aResult.Assign(gAppData->UAName);
return NS_OK;
}
NS_IMETHODIMP
nsXULAppInfo::GetSourceURL(nsACString& aResult) {
if (XRE_IsContentProcess()) {
ContentChild* cc = ContentChild::GetSingleton();
aResult = cc->GetAppInfo().sourceURL;
return NS_OK;
}
aResult.Assign(gAppData->sourceURL);
return NS_OK;
}
NS_IMETHODIMP
nsXULAppInfo::GetUpdateURL(nsACString& aResult) {
if (XRE_IsContentProcess()) {
ContentChild* cc = ContentChild::GetSingleton();
aResult = cc->GetAppInfo().updateURL;
return NS_OK;
}
aResult.Assign(gAppData->updateURL);
return NS_OK;
}
NS_IMETHODIMP
nsXULAppInfo::GetRemotingName(nsACString& aResult) {
if (XRE_IsContentProcess()) {
MOZ_ASSERT(false,
"nsXULAppInfo::remotingName should not be accessed from the "
"content process");
return NS_ERROR_UNEXPECTED;
}
aResult.Assign(gAppData->remotingName);
return NS_OK;
}
NS_IMETHODIMP
nsXULAppInfo::GetLogConsoleErrors(bool* aResult) {
*aResult = gLogConsoleErrors;
return NS_OK;
}
NS_IMETHODIMP
nsXULAppInfo::SetLogConsoleErrors(bool aValue) {
gLogConsoleErrors = aValue;
return NS_OK;
}
NS_IMETHODIMP
nsXULAppInfo::GetInSafeMode(bool* aResult) {
*aResult = gSafeMode;
return NS_OK;
}
NS_IMETHODIMP
nsXULAppInfo::GetOS(nsACString& aResult) {
aResult.AssignLiteral(OS_TARGET);
return NS_OK;
}
NS_IMETHODIMP
nsXULAppInfo::GetXPCOMABI(nsACString& aResult) {
#ifdef TARGET_XPCOM_ABI
aResult.AssignLiteral(TARGET_XPCOM_ABI);
return NS_OK;
#else
return NS_ERROR_NOT_AVAILABLE;
#endif
}
NS_IMETHODIMP
nsXULAppInfo::GetWidgetToolkit(nsACString& aResult) {
aResult.AssignLiteral(MOZ_WIDGET_TOOLKIT);
return NS_OK;
}
// Ensure that the GeckoProcessType enum, defined in xpcom/build/nsXULAppAPI.h,
// is synchronized with the const unsigned longs defined in
// xpcom/system/nsIXULRuntime.idl.
#define GECKO_PROCESS_TYPE(enum_value, enum_name, string_name, proc_typename, \
process_bin_type, procinfo_typename, \
webidl_typename, allcaps_name) \
static_assert(nsIXULRuntime::PROCESS_TYPE_##allcaps_name == \
static_cast<int>(GeckoProcessType_##enum_name), \
"GeckoProcessType in nsXULAppAPI.h not synchronized with " \
"nsIXULRuntime.idl");
#include "mozilla/GeckoProcessTypes.inc"
#undef GECKO_PROCESS_TYPE
// .. and ensure that that is all of them:
static_assert(GeckoProcessType_Utility + 1 == GeckoProcessType_End,
"Did not find the final GeckoProcessType");
NS_IMETHODIMP
nsXULAppInfo::GetProcessType(uint32_t* aResult) {
NS_ENSURE_ARG_POINTER(aResult);
*aResult = XRE_GetProcessType();
return NS_OK;
}
NS_IMETHODIMP
nsXULAppInfo::GetProcessID(uint32_t* aResult) {
#ifdef XP_WIN
*aResult = GetCurrentProcessId();
#else
*aResult = getpid();
#endif
return NS_OK;
}
NS_IMETHODIMP
nsXULAppInfo::GetUniqueProcessID(uint64_t* aResult) {
if (XRE_IsContentProcess()) {
ContentChild* cc = ContentChild::GetSingleton();
*aResult = cc->GetID();
} else {
*aResult = 0;
}
return NS_OK;
}
NS_IMETHODIMP
nsXULAppInfo::GetRemoteType(nsACString& aRemoteType) {
if (XRE_IsContentProcess()) {
aRemoteType = ContentChild::GetSingleton()->GetRemoteType().Stringify();
} else {
aRemoteType = dom::RemoteType::NotRemote().Stringify();
}
return NS_OK;
}
constinit static nsCString gLastAppVersion;
constinit static nsCString gLastAppBuildID;
// Whether the loaded profile's compatibility.ini carries EncryptedDatabases=1.
// Populated by CheckCompatibility() (which already parses compatibility.ini)
// and consulted by the encryption-compatibility gate in XRE_mainStartup.
static bool gProfileEncryptedDatabases = false;
NS_IMETHODIMP
nsXULAppInfo::GetLastAppVersion(nsACString& aResult) {
if (XRE_IsContentProcess()) {
return NS_ERROR_NOT_AVAILABLE;
}
if (!gLastAppVersion.IsVoid() && gLastAppVersion.IsEmpty()) {
NS_WARNING("Attempt to retrieve lastAppVersion before it has been set.");
return NS_ERROR_NOT_AVAILABLE;
}
aResult.Assign(gLastAppVersion);
return NS_OK;
}
NS_IMETHODIMP
nsXULAppInfo::GetLastAppBuildID(nsACString& aResult) {
if (XRE_IsContentProcess()) {
return NS_ERROR_NOT_AVAILABLE;
}
if (!gLastAppBuildID.IsVoid() && gLastAppBuildID.IsEmpty()) {
NS_WARNING("Attempt to retrieve lastAppBuildID before it has been set.");
return NS_ERROR_NOT_AVAILABLE;
}
aResult.Assign(gLastAppBuildID);
return NS_OK;
}
NS_IMETHODIMP
nsXULAppInfo::GetFissionAutostart(bool* aResult) {
*aResult = FissionAutostart();
return NS_OK;
}
NS_IMETHODIMP
nsXULAppInfo::GetWin32kExperimentStatus(ExperimentStatus* aResult) {
if (!XRE_IsParentProcess()) {
return NS_ERROR_NOT_AVAILABLE;
}
EnsureWin32kInitialized();
*aResult = gWin32kExperimentStatus;
return NS_OK;
}
NS_IMETHODIMP
nsXULAppInfo::GetWin32kLiveStatusTestingOnly(
nsIXULRuntime::ContentWin32kLockdownState* aResult) {
if (!XRE_IsParentProcess()) {
return NS_ERROR_NOT_AVAILABLE;
}
EnsureWin32kInitialized();
*aResult = GetLiveWin32kLockdownState();
return NS_OK;
}
NS_IMETHODIMP
nsXULAppInfo::GetWin32kSessionStatus(
nsIXULRuntime::ContentWin32kLockdownState* aResult) {
if (!XRE_IsParentProcess()) {
return NS_ERROR_NOT_AVAILABLE;
}
EnsureWin32kInitialized();
*aResult = gWin32kStatus;
return NS_OK;
}
NS_IMETHODIMP
nsXULAppInfo::GetFissionDecisionStatus(FissionDecisionStatus* aResult) {
if (!XRE_IsParentProcess()) {
return NS_ERROR_NOT_AVAILABLE;
}
EnsureFissionAutostartInitialized();
MOZ_ASSERT(gFissionDecisionStatus != eFissionStatusUnknown);
*aResult = gFissionDecisionStatus;
return NS_OK;
}
NS_IMETHODIMP
nsXULAppInfo::GetFissionDecisionStatusString(nsACString& aResult) {
if (!XRE_IsParentProcess()) {
return NS_ERROR_NOT_AVAILABLE;
}
EnsureFissionAutostartInitialized();
switch (gFissionDecisionStatus) {
case eFissionDisabledByE10sEnv:
aResult = "disabledByE10sEnv";
break;
case eFissionEnabledByEnv:
aResult = "enabledByEnv";
break;
case eFissionDisabledByEnv:
aResult = "disabledByEnv";
break;
case eFissionEnabledByDefault:
aResult = "enabledByDefault";
break;
case eFissionDisabledByDefault:
aResult = "disabledByDefault";
break;
case eFissionEnabledByUserPref:
aResult = "enabledByUserPref";
break;
case eFissionDisabledByUserPref:
aResult = "disabledByUserPref";
break;
case eFissionDisabledByE10sOther:
aResult = "disabledByE10sOther";
break;
default:
MOZ_ASSERT_UNREACHABLE("Unexpected enum value");
}
return NS_OK;
}
NS_IMETHODIMP
nsXULAppInfo::GetSessionStorePlatformCollection(bool* aResult) {
*aResult = SessionStorePlatformCollection();
return NS_OK;
}
NS_IMETHODIMP
nsXULAppInfo::GetBrowserTabsRemoteAutostart(bool* aResult) {
*aResult = BrowserTabsRemoteAutostart();
return NS_OK;
}
NS_IMETHODIMP
nsXULAppInfo::GetMaxWebProcessCount(uint32_t* aResult) {
*aResult = mozilla::GetMaxWebProcessCount();
return NS_OK;
}
NS_IMETHODIMP
nsXULAppInfo::GetAccessibilityEnabled(bool* aResult) {
#ifdef ACCESSIBILITY
*aResult = GetAccService() != nullptr;
#else
*aResult = false;
#endif
return NS_OK;
}
NS_IMETHODIMP
nsXULAppInfo::GetAccessibilityInstantiator(nsAString& aInstantiator) {
aInstantiator.Truncate();
#if defined(ACCESSIBILITY)
if (GetAccService()) {
a11y::GetHumanReadableInstantiatorStr(aInstantiator);
# if defined(XP_WIN)
aInstantiator.AppendLiteral("|");
nsCOMPtr<nsIFile> oopClientExe;
if (a11y::GetInstantiator(getter_AddRefs(oopClientExe))) {
nsAutoString oopClientInfo;
if (NS_SUCCEEDED(oopClientExe->GetPath(oopClientInfo))) {
aInstantiator.Append(oopClientInfo);
}
}
# endif
}
#endif
return NS_OK;
}
NS_IMETHODIMP
nsXULAppInfo::GetIs64Bit(bool* aResult) {
#ifdef HAVE_64BIT_BUILD
*aResult = true;
#else
*aResult = false;
#endif
return NS_OK;
}
NS_IMETHODIMP
nsXULAppInfo::GetIsTextRecognitionSupported(bool* aResult) {
*aResult = widget::TextRecognition::IsSupported();
return NS_OK;
}
NS_IMETHODIMP
nsXULAppInfo::InvalidateCachesOnRestart() {
nsCOMPtr<nsIFile> file;
nsresult rv =
NS_GetSpecialDirectory(NS_APP_PROFILE_DIR_STARTUP, getter_AddRefs(file));
if (NS_FAILED(rv)) return rv;
if (!file) return NS_ERROR_NOT_AVAILABLE;
file->AppendNative(FILE_COMPATIBILITY_INFO);
nsINIParser parser;
rv = parser.Init(file);
if (NS_FAILED(rv)) {
// This fails if compatibility.ini is not there, so we'll
// flush the caches on the next restart anyways.
return NS_OK;
}
nsAutoCString buf;
rv = parser.GetString("Compatibility", "InvalidateCaches", buf);
if (NS_FAILED(rv)) {
PRFileDesc* fd;
rv = file->OpenNSPRFileDesc(PR_RDWR | PR_APPEND, 0600, &fd);
if (NS_FAILED(rv)) {
NS_ERROR("could not create output stream");
return NS_ERROR_NOT_AVAILABLE;
}
static const char kInvalidationHeader[] =
NS_LINEBREAK "InvalidateCaches=1" NS_LINEBREAK;
PR_Write(fd, kInvalidationHeader, sizeof(kInvalidationHeader) - 1);
PR_Close(fd);
}
return NS_OK;
}
nsresult mozilla::MarkProfileEncryptedDatabases() {
// Append the EncryptedDatabases marker to compatibility.ini (modeled on
// InvalidateCachesOnRestart above). Read back by CheckCompatibility() on the
// next startup so the encryption gate can refuse to launch a build whose
// pref disagrees with the now-encrypted profile. Append-only: the marker is
// never removed (a profile that has held encrypted databases stays marked).
nsCOMPtr<nsIFile> file;
nsresult rv =
NS_GetSpecialDirectory(NS_APP_PROFILE_DIR_STARTUP, getter_AddRefs(file));
// No resolvable startup profile directory (xpcshell, or a background task /
// very early startup with no profile yet): there is no persistent
// compatibility.ini to gate a future launch, so there is nothing to mark and
// nothing to protect. Treat this as a benign no-op. Returning a failure here
// would flip sMarkerWriteFailed (see MarkProfileEncryptedIfNeeded) and refuse
// every DEK mint for the session, breaking all in-profile opens under
// xpcshell with the pref on. A genuine write failure on an existing profile
// is still surfaced below.
if (NS_FAILED(rv) || !file) return NS_OK;
file->AppendNative(FILE_COMPATIBILITY_INFO);
nsINIParser parser;
rv = parser.Init(file);
if (NS_FAILED(rv)) {
// No compatibility.ini yet (WriteVersion creates it once per profile);
// the marker will be written on a later startup.
return NS_OK;
}
nsAutoCString buf;
rv = parser.GetString("Compatibility", "EncryptedDatabases", buf);
if (NS_SUCCEEDED(rv)) {
// Already marked.
return NS_OK;
}
PRFileDesc* fd;
rv = file->OpenNSPRFileDesc(PR_RDWR | PR_APPEND, 0600, &fd);
if (NS_FAILED(rv)) {
return NS_ERROR_NOT_AVAILABLE;
}
static const char kEncryptedHeader[] =
NS_LINEBREAK "EncryptedDatabases=1" NS_LINEBREAK;
PR_Write(fd, kEncryptedHeader, sizeof(kEncryptedHeader) - 1);
PR_Close(fd);
return NS_OK;
}
NS_IMETHODIMP
nsXULAppInfo::MarkProfileEncryptedDatabases() {
return mozilla::MarkProfileEncryptedDatabases();
}
NS_IMETHODIMP
nsXULAppInfo::GetReplacedLockTime(PRTime* aReplacedLockTime) {
if (!gProfileLock) return NS_ERROR_NOT_AVAILABLE;
gProfileLock->GetReplacedLockTime(aReplacedLockTime);
return NS_OK;
}
NS_IMETHODIMP
nsXULAppInfo::GetDefaultUpdateChannel(nsACString& aResult) {
aResult.AssignLiteral(MOZ_STRINGIFY(MOZ_UPDATE_CHANNEL));
return NS_OK;
}
NS_IMETHODIMP
nsXULAppInfo::GetDistributionID(nsACString& aResult) {
aResult.AssignLiteral(MOZ_DISTRIBUTION_ID);
return NS_OK;
}
NS_IMETHODIMP
nsXULAppInfo::GetWindowsDLLBlocklistStatus(bool* aResult) {
#if defined(HAS_DLL_BLOCKLIST)
*aResult = DllBlocklist_CheckStatus();
#else
*aResult = false;
#endif
return NS_OK;
}
NS_IMETHODIMP
nsXULAppInfo::GetRestartedByOS(bool* aResult) {
*aResult = gRestartedByOS;
return NS_OK;
}
NS_IMETHODIMP
nsXULAppInfo::GetChromeColorSchemeIsDark(bool* aResult) {
*aResult = PreferenceSheet::ColorSchemeForChrome() == ColorScheme::Dark;
return NS_OK;
}
NS_IMETHODIMP
nsXULAppInfo::GetNativeMenubar(bool* aResult) {
*aResult = !!LookAndFeel::GetInt(LookAndFeel::IntID::NativeMenubar);
return NS_OK;
}
NS_IMETHODIMP
nsXULAppInfo::GetContentThemeDerivedColorSchemeIsDark(bool* aResult) {
*aResult =
PreferenceSheet::ThemeDerivedColorSchemeForContent() == ColorScheme::Dark;
return NS_OK;
}
NS_IMETHODIMP
nsXULAppInfo::GetPrefersReducedMotion(bool* aResult) {
*aResult =
LookAndFeel::GetInt(LookAndFeel::IntID::PrefersReducedMotion, 0) == 1;
return NS_OK;
}
NS_IMETHODIMP
nsXULAppInfo::GetDrawInTitlebar(bool* aResult) {
*aResult = LookAndFeel::DrawInTitlebar();
return NS_OK;
}
NS_IMETHODIMP
nsXULAppInfo::GetCaretBlinkCount(int32_t* aResult) {
*aResult = LookAndFeel::CaretBlinkCount();
return NS_OK;
}
NS_IMETHODIMP
nsXULAppInfo::GetCaretBlinkTime(int32_t* aResult) {
*aResult = LookAndFeel::CaretBlinkTime();
return NS_OK;
}
NS_IMETHODIMP
nsXULAppInfo::GetDesktopEnvironment(nsACString& aDesktopEnvironment) {
#ifdef MOZ_WIDGET_GTK
aDesktopEnvironment.Assign(GetDesktopEnvironmentIdentifier());
#endif
return NS_OK;
}
NS_IMETHODIMP
nsXULAppInfo::GetIsWayland(bool* aResult) {
#ifdef MOZ_WIDGET_GTK
*aResult = GdkIsWaylandDisplay();
#else
*aResult = false;
#endif
return NS_OK;
}
NS_IMETHODIMP
nsXULAppInfo::GetProcessStartupShortcut(nsAString& aShortcut) {
#if defined(XP_WIN)
if (XRE_IsParentProcess()) {
aShortcut.Assign(gProcessStartupShortcut);
return NS_OK;
}
#endif
return NS_ERROR_NOT_AVAILABLE;
}
#if defined(XP_WIN) && defined(MOZ_LAUNCHER_PROCESS)
// Forward declaration
void SetupLauncherProcessPref();
static Maybe<LauncherRegistryInfo::EnabledState> gLauncherProcessState;
#endif // defined(XP_WIN) && defined(MOZ_LAUNCHER_PROCESS)
NS_IMETHODIMP
nsXULAppInfo::GetLauncherProcessState(uint32_t* aResult) {
#if defined(XP_WIN) && defined(MOZ_LAUNCHER_PROCESS)
SetupLauncherProcessPref();
if (!gLauncherProcessState) {
return NS_ERROR_UNEXPECTED;
}
*aResult = static_cast<uint32_t>(gLauncherProcessState.value());
return NS_OK;
#else
return NS_ERROR_NOT_AVAILABLE;
#endif
}
#ifdef XP_WIN
NS_IMETHODIMP
nsXULAppInfo::GetUserCanElevate(bool* aUserCanElevate) {
HANDLE rawToken;
if (!::OpenProcessToken(::GetCurrentProcess(), TOKEN_QUERY, &rawToken)) {
*aUserCanElevate = false;
return NS_OK;
}
nsAutoHandle token(rawToken);
LauncherResult<TOKEN_ELEVATION_TYPE> elevationType = GetElevationType(token);
if (elevationType.isErr()) {
*aUserCanElevate = false;
return NS_OK;
}
// The possible values returned for elevationType and their meanings are:
// TokenElevationTypeDefault: The token does not have a linked token
// (e.g. UAC disabled or a standard user, so they can't be elevated)
// TokenElevationTypeFull: The token is linked to an elevated token
// (e.g. UAC is enabled and the user is already elevated so they can't
// be elevated again)
// TokenElevationTypeLimited: The token is linked to a limited token
// (e.g. UAC is enabled and the user is not elevated, so they can be
// elevated)
*aUserCanElevate = (elevationType.inspect() == TokenElevationTypeLimited);
return NS_OK;
}
#endif
// Get the CrashReporter::Annotation referred to by the `aKey` string. This
// wraps `CrashReporter::AnnotationFromString` to assert the annotation string
// is valid in debug builds, and otherwise returns an NS_ERROR_INVALID_ARG if
// it is invalid.
static Result<CrashReporter::Annotation, nsresult> GetCrashAnnotation(
const nsACString& aKey) {
auto maybeAnnotation = CrashReporter::AnnotationFromString(aKey);
#ifdef DEBUG
if (!maybeAnnotation.isSome()) {
std::string warning = "Annotation identifier not found: \"";
warning += aKey.View();
warning += "\"";
NS_WARNING(warning.c_str());
}
if (!StaticPrefs::toolkit_crash_annotation_testing_validation()) {
MOZ_ASSERT(maybeAnnotation.isSome(),
"Invalid annotation identifier; ensure it exists in "
"CrashAnnotations.yaml");
}
#endif
NS_ENSURE_TRUE(maybeAnnotation.isSome(), Err(NS_ERROR_INVALID_ARG));
return maybeAnnotation.extract();
}
NS_IMETHODIMP
nsXULAppInfo::GetCrashReporterEnabled(bool* aEnabled) {
*aEnabled = CrashReporter::GetEnabled();
return NS_OK;
}
NS_IMETHODIMP
nsXULAppInfo::SetEnabled(bool aEnabled) {
if (aEnabled) {
if (CrashReporter::GetEnabled()) {
// no point in erroring for double-enabling
return NS_OK;
}
nsCOMPtr<nsIFile> greBinDir;
NS_GetSpecialDirectory(NS_GRE_BIN_DIR, getter_AddRefs(greBinDir));
if (!greBinDir) {
return NS_ERROR_FAILURE;
}
nsCOMPtr<nsIFile> xreBinDirectory = greBinDir;
if (!xreBinDirectory) {
return NS_ERROR_FAILURE;
}
nsresult rv = CrashReporter::OOPInit(xreBinDirectory, true);
if (rv != NS_OK) {
return rv;
}
return CrashReporter::SetExceptionHandler(xreBinDirectory, true);
}
if (!CrashReporter::GetEnabled()) {
// no point in erroring for double-disabling
return NS_OK;
}
nsresult rv = CrashReporter::UnsetExceptionHandler();
#if !defined(MOZ_WIDGET_ANDROID)
// Don't deinit on Android as we can't get back up again
// TODO: Fix it
CrashReporter::OOPDeinit();
#endif
return rv;
}
NS_IMETHODIMP
nsXULAppInfo::GetServerURL(nsIURL** aServerURL) {
NS_ENSURE_ARG_POINTER(aServerURL);
if (!CrashReporter::GetEnabled()) return NS_ERROR_NOT_INITIALIZED;
nsAutoCString data;
if (!CrashReporter::GetServerURL(data)) {
return NS_ERROR_FAILURE;
}
nsCOMPtr<nsIURI> uri;
NS_NewURI(getter_AddRefs(uri), data);
if (!uri) return NS_ERROR_FAILURE;
nsCOMPtr<nsIURL> url;
url = do_QueryInterface(uri);
NS_ADDREF(*aServerURL = url);
return NS_OK;
}
NS_IMETHODIMP
nsXULAppInfo::SetServerURL(nsIURL* aServerURL) {
// Only allow https or http URLs
if (!net::SchemeIsHttpOrHttps(aServerURL)) {
return NS_ERROR_INVALID_ARG;
}
nsAutoCString spec;
nsresult rv = aServerURL->GetSpec(spec);
NS_ENSURE_SUCCESS(rv, rv);
return CrashReporter::SetServerURL(spec);
}
NS_IMETHODIMP
nsXULAppInfo::GetMinidumpPath(nsIFile** aMinidumpPath) {
if (!CrashReporter::GetEnabled()) {
return NS_ERROR_NOT_INITIALIZED;
}
nsAutoString path;
if (!CrashReporter::GetMinidumpPath(path)) {
return NS_ERROR_FAILURE;
}
return NS_NewLocalFile(path, aMinidumpPath);
}
NS_IMETHODIMP
nsXULAppInfo::SetMinidumpPath(nsIFile* aMinidumpPath) {
nsAutoString path;
nsresult rv = aMinidumpPath->GetPath(path);
NS_ENSURE_SUCCESS(rv, rv);
return CrashReporter::SetMinidumpPath(path);
}
NS_IMETHODIMP
nsXULAppInfo::GetMinidumpForID(const nsAString& aId, nsIFile** aMinidump) {
if (!CrashReporter::GetMinidumpForID(aId, aMinidump)) {
return NS_ERROR_FILE_NOT_FOUND;
}
return NS_OK;
}
NS_IMETHODIMP
nsXULAppInfo::GetExtraFileForID(const nsAString& aId, nsIFile** aExtraFile) {
if (!CrashReporter::GetExtraFileForID(aId, aExtraFile)) {
return NS_ERROR_FILE_NOT_FOUND;
}
return NS_OK;
}
NS_IMETHODIMP
nsXULAppInfo::AnnotateCrashReport(const nsACString& key,
JS::Handle<JS::Value> data, JSContext* cx) {
CrashReporter::Annotation annotation = MOZ_TRY(GetCrashAnnotation(key));
switch (data.type()) {
case JS::ValueType::Int32:
CrashReporter::RecordAnnotationU32(annotation, data.toInt32());
break;
case JS::ValueType::Boolean:
CrashReporter::RecordAnnotationBool(annotation, data.toBoolean());
break;
case JS::ValueType::String: {
JSString* value = data.toString();
size_t length = (JS_GetStringLength(value) * 3) + 1;
nsCString buffer;
auto res = JS_EncodeStringToUTF8BufferPartial(
cx, value, buffer.GetMutableData(length));
if (!res) {
return NS_ERROR_OUT_OF_MEMORY;
}
size_t written;
std::tie(std::ignore, written) = *res;
buffer.SetLength(written);
CrashReporter::RecordAnnotationNSCString(annotation, buffer);
} break;
default:
return NS_ERROR_INVALID_ARG;
}
return NS_OK;
}
NS_IMETHODIMP
nsXULAppInfo::RemoveCrashReportAnnotation(const nsACString& key) {
CrashReporter::Annotation annotation = MOZ_TRY(GetCrashAnnotation(key));
CrashReporter::UnrecordAnnotation(annotation);
return NS_OK;
}
NS_IMETHODIMP
nsXULAppInfo::IsAnnotationValid(const nsACString& aValue, bool* aIsValid) {
auto annotation = CrashReporter::AnnotationFromString(aValue);
*aIsValid = annotation.isSome();
return NS_OK;
}
NS_IMETHODIMP
nsXULAppInfo::IsAnnotationAllowedForReport(const nsACString& aValue,
bool* aIsAllowed) {
CrashReporter::Annotation annotation = MOZ_TRY(GetCrashAnnotation(aValue));
*aIsAllowed = CrashReporter::IsAnnotationAllowedForReport(annotation);
return NS_OK;
}
NS_IMETHODIMP
nsXULAppInfo::AppendAppNotesToCrashReport(const nsACString& data) {
return CrashReporter::AppendAppNotesToCrashReport(data);
}
NS_IMETHODIMP
nsXULAppInfo::WriteMinidumpForException(void* aExceptionInfo) {
#ifdef XP_WIN
return CrashReporter::WriteMinidumpForException(
static_cast<EXCEPTION_POINTERS*>(aExceptionInfo));
#else
return NS_ERROR_NOT_IMPLEMENTED;
#endif
}
NS_IMETHODIMP
nsXULAppInfo::AppendObjCExceptionInfoToAppNotes(void* aException) {
#ifdef XP_MACOSX
return CrashReporter::AppendObjCExceptionInfoToAppNotes(aException);
#else
return NS_ERROR_NOT_IMPLEMENTED;
#endif
}
NS_IMETHODIMP
nsXULAppInfo::GetSubmitReports(bool* aEnabled) {
return CrashReporter::GetSubmitReports(aEnabled);
}
NS_IMETHODIMP
nsXULAppInfo::SetSubmitReports(bool aEnabled) {
return CrashReporter::SetSubmitReports(aEnabled);
}
NS_IMETHODIMP
nsXULAppInfo::UpdateCrashEventsDir() {
CrashReporter::UpdateCrashEventsDir();
return NS_OK;
}
NS_IMETHODIMP
nsXULAppInfo::SaveMemoryReport() {
if (!CrashReporter::GetEnabled()) {
return NS_ERROR_NOT_INITIALIZED;
}
nsCOMPtr<nsIFile> file;
nsresult rv = CrashReporter::GetDefaultMemoryReportFile(getter_AddRefs(file));
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
nsString path;
file->GetPath(path);
nsCOMPtr<nsIMemoryInfoDumper> dumper =
do_GetService("@mozilla.org/memory-info-dumper;1");
if (NS_WARN_IF(!dumper)) {
return NS_ERROR_UNEXPECTED;
}
rv = dumper->DumpMemoryReportsToNamedFile(
path, this, file, true /* anonymize */, false /* minimizeMemoryUsage */);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
return NS_OK;
}
// This method is from nsIFInishDumpingCallback.
NS_IMETHODIMP
nsXULAppInfo::Callback(nsISupports* aData) {
nsCOMPtr<nsIFile> file = do_QueryInterface(aData);
MOZ_ASSERT(file);
CrashReporter::SetMemoryReportFile(file);
return NS_OK;
}
static const nsXULAppInfo kAppInfo;
namespace mozilla {
nsresult AppInfoConstructor(REFNSIID aIID, void** aResult) {
return const_cast<nsXULAppInfo*>(&kAppInfo)->QueryInterface(aIID, aResult);
}
} // namespace mozilla
bool gLogConsoleErrors = false;
#define NS_ENSURE_TRUE_LOG(x, ret) \
PR_BEGIN_MACRO \
if (MOZ_UNLIKELY(!(x))) { \
NS_WARNING("NS_ENSURE_TRUE(" #x ") failed"); \
gLogConsoleErrors = true; \
return ret; \
} \
PR_END_MACRO
#define NS_ENSURE_SUCCESS_LOG(res, ret) \
NS_ENSURE_TRUE_LOG(NS_SUCCEEDED(res), ret)
/**
* Because we're starting/stopping XPCOM several times in different scenarios,
* this class is a stack-based critter that makes sure that XPCOM is shut down
* during early returns.
*/
class ScopedXPCOMStartup {
public:
ScopedXPCOMStartup() : mServiceManager(nullptr) {}
~ScopedXPCOMStartup();
nsresult Initialize(bool aInitJSContext = true);
nsresult SetWindowCreator(nsINativeAppSupport* native);
private:
nsIServiceManager* mServiceManager;
static nsINativeAppSupport* gNativeAppSupport;
friend already_AddRefed<nsINativeAppSupport> NS_GetNativeAppSupport();
};
ScopedXPCOMStartup::~ScopedXPCOMStartup() {
NS_IF_RELEASE(gNativeAppSupport);
if (mServiceManager) {
#ifdef XP_MACOSX
// On OS X, we need a pool to catch cocoa objects that are autoreleased
// during teardown.
mozilla::MacAutoreleasePool pool;
#endif
nsCOMPtr<nsIAppStartup> appStartup(components::AppStartup::Service());
if (appStartup) appStartup->DestroyHiddenWindow();
gDirServiceProvider->DoShutdown();
PROFILER_MARKER_UNTYPED("Shutdown early", OTHER);
WriteConsoleLog();
NS_ShutdownXPCOM(mServiceManager);
mServiceManager = nullptr;
}
}
nsresult ScopedXPCOMStartup::Initialize(bool aInitJSContext) {
NS_ASSERTION(gDirServiceProvider, "Should not get here!");
nsresult rv;
rv = NS_InitXPCOM(&mServiceManager, gDirServiceProvider->GetAppDir(),
gDirServiceProvider, aInitJSContext);
if (NS_FAILED(rv)) {
NS_ERROR("Couldn't start xpcom!");
mServiceManager = nullptr;
} else {
#ifdef DEBUG
nsCOMPtr<nsIComponentRegistrar> reg = do_QueryInterface(mServiceManager);
NS_ASSERTION(reg, "Service Manager doesn't QI to Registrar.");
#endif
}
return rv;
}
/**
* This is a little factory class that serves as a singleton-service-factory
* for the nativeappsupport object.
*/
class nsSingletonFactory final : public nsIFactory {
public:
NS_DECL_ISUPPORTS
NS_DECL_NSIFACTORY
explicit nsSingletonFactory(nsISupports* aSingleton);
private:
~nsSingletonFactory() = default;
nsCOMPtr<nsISupports> mSingleton;
};
nsSingletonFactory::nsSingletonFactory(nsISupports* aSingleton)
: mSingleton(aSingleton) {
NS_ASSERTION(mSingleton, "Singleton was null!");
}
NS_IMPL_ISUPPORTS(nsSingletonFactory, nsIFactory)
NS_IMETHODIMP
nsSingletonFactory::CreateInstance(const nsIID& aIID, void** aResult) {
return mSingleton->QueryInterface(aIID, aResult);
}
/**
* Set our windowcreator on the WindowWatcher service.
*/
nsresult ScopedXPCOMStartup::SetWindowCreator(nsINativeAppSupport* native) {
nsresult rv;
NS_IF_ADDREF(gNativeAppSupport = native);
nsCOMPtr<nsIWindowCreator> creator(components::AppStartup::Service());
if (!creator) return NS_ERROR_UNEXPECTED;
nsCOMPtr<nsIWindowWatcher> wwatch(
do_GetService(NS_WINDOWWATCHER_CONTRACTID, &rv));
NS_ENSURE_SUCCESS(rv, rv);
return wwatch->SetWindowCreator(creator);
}
/* static */ already_AddRefed<nsINativeAppSupport> NS_GetNativeAppSupport() {
if (!ScopedXPCOMStartup::gNativeAppSupport) {
return nullptr;
}
return do_AddRef(ScopedXPCOMStartup::gNativeAppSupport);
}
#ifdef MOZ_HAS_REMOTE
/* static */ already_AddRefed<nsIRemoteService> GetRemoteService() {
AssertIsOnMainThread();
if (!gRemoteService) {
gRemoteService = new nsRemoteService();
}
nsCOMPtr<nsIRemoteService> remoteService = gRemoteService.get();
return remoteService.forget();
}
#endif
nsINativeAppSupport* ScopedXPCOMStartup::gNativeAppSupport;
static void DumpArbitraryHelp() {
nsresult rv;
ScopedLogging log;
{
ScopedXPCOMStartup xpcom;
xpcom.Initialize();
nsCOMPtr<nsICommandLineRunner> cmdline(new nsCommandLine());
nsCString text;
rv = cmdline->GetHelpText(text);
if (NS_SUCCEEDED(rv)) printf("%s", text.get());
}
}
// English text needs to go into a dtd file.
// But when this is called we have no components etc. These strings must either
// be here, or in a native resource file.
static void DumpHelp() {
printf(
"Usage: %s [ options ... ] [URL]\n"
" where options include:\n\n",
gArgv[0]);
#ifdef MOZ_X11
printf(
"X11 options\n"
" --display=DISPLAY X display to use\n"
" --sync Make X calls synchronous\n");
#endif
#ifdef XP_UNIX
printf(
" --g-fatal-warnings Make all warnings fatal\n"
"\n%s options\n",
(const char*)gAppData->name);
#endif
printf(
" -h or --help Print this message.\n"
" -v or --version Print %s version.\n"
" --full-version Print %s version, build and platform build ids.\n"
" -P <profile> Start with <profile>.\n"
" --profile <path> Start with profile at <path>.\n"
" --migration Start with migration wizard.\n"
" --ProfileManager Start with ProfileManager.\n"
" --origin-to-force-quic-on <origin>\n"
" Force to use QUIC for the specified origin.\n"
#ifdef MOZ_HAS_REMOTE
" --new-instance Open new instance, not a new window in running "
"instance.\n"
#endif
" --safe-mode Disables extensions and themes for this session.\n"
#ifdef MOZ_BLOCK_PROFILE_DOWNGRADE
" --allow-downgrade Allows downgrading a profile.\n"
#endif
" --MOZ_LOG=<modules> Treated as MOZ_LOG=<modules> environment "
"variable,\n"
" overrides it.\n"
" --MOZ_LOG_FILE=<file> Treated as MOZ_LOG_FILE=<file> environment "
"variable,\n"
" overrides it. If MOZ_LOG_FILE is not specified as "
"an\n"
" argument or as an environment variable, logging "
"will be\n"
" written to stdout.\n",
(const char*)gAppData->name, (const char*)gAppData->name);
#if defined(XP_WIN)
printf(" --console Start %s with a debugging console.\n",
(const char*)gAppData->name);
#endif
#if defined(XP_WIN) || defined(MOZ_WIDGET_GTK) || defined(XP_MACOSX)
printf(" --headless Run without a GUI.\n");
#endif
#if defined(MOZ_ENABLE_DBUS)
printf(
" --dbus-service <launcher> Run as DBus service for "
"org.freedesktop.Application and\n"
" set a launcher (usually /usr/bin/appname "
"script) for it.\n");
#endif
// this works, but only after the components have registered. so if you drop
// in a new command line handler, --help won't not until the second run. out
// of the bug, because we ship a component.reg file, it works correctly.
DumpArbitraryHelp();
}
static inline void DumpVersion() {
if (gAppData->vendor && *gAppData->vendor) {
printf("%s ", (const char*)gAppData->vendor);
}
printf("%s ", (const char*)gAppData->name);
// Use the displayed version
// For example, for beta, we would display 42.0b2 instead of 42.0
printf("%s", MOZ_STRINGIFY(MOZ_APP_VERSION_DISPLAY));
if (gAppData->copyright && *gAppData->copyright) {
printf(", %s", (const char*)gAppData->copyright);
}
printf("\n");
}
static inline void DumpFullVersion() {
if (gAppData->vendor && *gAppData->vendor) {
printf("%s ", (const char*)gAppData->vendor);
}
printf("%s ", (const char*)gAppData->name);
// Use the displayed version
// For example, for beta, we would display 42.0b2 instead of 42.0
printf("%s ", MOZ_STRINGIFY(MOZ_APP_VERSION_DISPLAY));
printf("%s ", (const char*)gAppData->buildID);
printf("%s ", (const char*)PlatformBuildID());
if (gAppData->copyright && *gAppData->copyright) {
printf(", %s", (const char*)gAppData->copyright);
}
printf("\n");
}
void XRE_InitOmnijar(nsIFile* greOmni, nsIFile* appOmni) {
mozilla::Omnijar::Init(greOmni, appOmni);
}
nsresult XRE_GetBinaryPath(nsIFile** aResult) {
return mozilla::BinaryPath::GetFile(aResult);
}
#ifdef XP_WIN
# include "nsWindowsRestart.cpp"
# include <shellapi.h>
typedef BOOL(WINAPI* SetProcessDEPPolicyFunc)(DWORD dwFlags);
static void RegisterApplicationRestartChanged(const char* aPref, void* aData) {
DWORD cchCmdLine = 0;
HRESULT rc = ::GetApplicationRestartSettings(::GetCurrentProcess(), nullptr,
&cchCmdLine, nullptr);
bool wasRegistered = false;
if (rc == S_OK) {
wasRegistered = true;
}
if (Preferences::GetBool(PREF_WIN_REGISTER_APPLICATION_RESTART, false) &&
!wasRegistered) {
// Make the command line to use when restarting.
// Excludes argv[0] because RegisterApplicationRestart adds the
// executable name, replace that temporarily with -os-restarted
char* exeName = gRestartArgv[0];
gRestartArgv[0] = const_cast<char*>("-os-restarted");
wchar_t** restartArgvConverted =
AllocConvertUTF8toUTF16Strings(gRestartArgc, gRestartArgv);
gRestartArgv[0] = exeName;
mozilla::UniquePtr<wchar_t[]> restartCommandLine;
if (restartArgvConverted) {
restartCommandLine =
mozilla::MakeCommandLine(gRestartArgc, restartArgvConverted);
FreeAllocStrings(gRestartArgc, restartArgvConverted);
}
if (restartCommandLine) {
// Flags RESTART_NO_PATCH and RESTART_NO_REBOOT are not set, so we
// should be restarted if terminated by an update or restart.
::RegisterApplicationRestart(restartCommandLine.get(),
RESTART_NO_CRASH | RESTART_NO_HANG);
}
} else if (wasRegistered) {
::UnregisterApplicationRestart();
}
}
static void OnAlteredPrefetchPrefChanged(const char* aPref, void* aData) {
int32_t prefVal = Preferences::GetInt(PREF_WIN_ALTERED_DLL_PREFETCH, 0);
mozilla::DllPrefetchExperimentRegistryInfo prefetchRegInfo;
mozilla::DebugOnly<mozilla::Result<Ok, nsresult>> reflectResult =
prefetchRegInfo.ReflectPrefToRegistry(prefVal);
MOZ_ASSERT(reflectResult.value.isOk());
}
static void SetupAlteredPrefetchPref() {
mozilla::DllPrefetchExperimentRegistryInfo prefetchRegInfo;
mozilla::DebugOnly<mozilla::Result<Ok, nsresult>> reflectResult =
prefetchRegInfo.ReflectPrefToRegistry(
Preferences::GetInt(PREF_WIN_ALTERED_DLL_PREFETCH, 0));
MOZ_ASSERT(reflectResult.value.isOk());
Preferences::RegisterCallback(&OnAlteredPrefetchPrefChanged,
PREF_WIN_ALTERED_DLL_PREFETCH);
}
static LazyLogModule gSkeletonLog("PreXULSkeletonUI");
# define SKELETON_LOG(str, ...) \
MOZ_LOG(gSkeletonLog, LogLevel::Debug, (str, ##__VA_ARGS__))
static void ReflectSkeletonUIPrefToRegistry(const char* aPref, void* aData) {
(void)aPref;
(void)aData;
RefPtr<nsToolkitProfileService> mProfileSvc;
mProfileSvc = NS_GetToolkitProfileService();
bool hasShowProfileSelector = mProfileSvc->HasShowProfileSelector();
bool skeletonUIPref = StaticPrefs::browser_startup_preXulSkeletonUI();
bool startupBlankWindowPref = StaticPrefs::browser_startup_blankWindow();
bool drawInTitlebar = LookAndFeel::DrawInTitlebar();
SKELETON_LOG(
"ReflectSkeletonUIPrefToRegistry: hasShowProfileSelector %d, "
"skeletonUIPref %d, startupBlankWindowPref %d, drawInTitlebar %d",
hasShowProfileSelector ? 1 : 0, skeletonUIPref ? 1 : 0,
startupBlankWindowPref ? 1 : 0, drawInTitlebar ? 1 : 0);
bool shouldBeEnabled = !hasShowProfileSelector && skeletonUIPref &&
startupBlankWindowPref && drawInTitlebar;
SKELETON_LOG("ReflectSkeletonUIPrefToRegistry: shouldBeEnabled %d",
shouldBeEnabled ? 1 : 0);
if (shouldBeEnabled && Preferences::HasUserValue(kPrefThemeId)) {
nsCString themeId;
Preferences::GetCString(kPrefThemeId, themeId);
if (themeId.EqualsLiteral("default-theme@mozilla.org")) {
(void)SetPreXULSkeletonUIThemeId(ThemeMode::Default);
} else if (themeId.EqualsLiteral("firefox-compact-dark@mozilla.org")) {
(void)SetPreXULSkeletonUIThemeId(ThemeMode::Dark);
} else if (themeId.EqualsLiteral("firefox-compact-light@mozilla.org")) {
(void)SetPreXULSkeletonUIThemeId(ThemeMode::Light);
} else {
SKELETON_LOG(
"ReflectSkeletonUIPrefToRegistry: clearing shouldBeEnabled "
"because of bad themeId %s",
themeId.get());
shouldBeEnabled = false;
}
} else if (shouldBeEnabled) {
(void)SetPreXULSkeletonUIThemeId(ThemeMode::Default);
}
SKELETON_LOG(
"ReflectSkeletonUIPrefToRegistry: old enabled %d, new enabled "
"%d",
GetPreXULSkeletonUIEnabled() ? 1 : 0, shouldBeEnabled ? 1 : 0);
if (GetPreXULSkeletonUIEnabled() != shouldBeEnabled) {
(void)SetPreXULSkeletonUIEnabledIfAllowed(shouldBeEnabled);
}
}
# undef SKELETON_LOG
class ShowProfileSelectorObserver final : public nsIObserver {
public:
NS_DECL_ISUPPORTS
NS_DECL_NSIOBSERVER
ShowProfileSelectorObserver() = default;
protected:
~ShowProfileSelectorObserver() = default;
};
NS_IMPL_ISUPPORTS(ShowProfileSelectorObserver, nsIObserver);
NS_IMETHODIMP
ShowProfileSelectorObserver::Observe(nsISupports* aSubject, const char* aTopic,
const char16_t* aData) {
(void)aSubject;
(void)aData;
if (!strcmp(aTopic, "profile-show-selector-changed")) {
ReflectSkeletonUIPrefToRegistry(nullptr, nullptr);
}
return NS_OK;
}
# if defined(MOZ_DEFAULT_BROWSER_AGENT)
static void SetupSkeletonUIPrefs() {
ReflectSkeletonUIPrefToRegistry(nullptr, nullptr);
Preferences::RegisterCallback(&ReflectSkeletonUIPrefToRegistry,
kPrefPreXulSkeletonUI);
Preferences::RegisterCallback(&ReflectSkeletonUIPrefToRegistry,
kPrefBrowserStartupBlankWindow);
Preferences::RegisterCallback(&ReflectSkeletonUIPrefToRegistry, kPrefThemeId);
Preferences::RegisterCallback(
&ReflectSkeletonUIPrefToRegistry,
nsDependentCString(StaticPrefs::GetPrefName_browser_tabs_inTitlebar()));
nsCOMPtr<nsIObserverService> obsService =
mozilla::services::GetObserverService();
nsCOMPtr<nsIObserver> obs = new ShowProfileSelectorObserver();
obsService->AddObserver(obs, "profile-show-selector-changed", false);
}
# endif // defined(MOZ_DEFAULT_BROWSER_AGENT)
# if defined(MOZ_LAUNCHER_PROCESS)
static void OnLauncherPrefChanged(const char* aPref, void* aData) {
bool prefVal = Preferences::GetBool(PREF_WIN_LAUNCHER_PROCESS_ENABLED, true);
mozilla::LauncherRegistryInfo launcherRegInfo;
mozilla::DebugOnly<mozilla::LauncherVoidResult> reflectResult =
launcherRegInfo.ReflectPrefToRegistry(prefVal);
MOZ_ASSERT(reflectResult.inspect().isOk());
}
static void OnLauncherTelemetryPrefChanged(const char* aPref, void* aData) {
bool prefVal = Preferences::GetBool(kPrefHealthReportUploadEnabled, true);
mozilla::LauncherRegistryInfo launcherRegInfo;
mozilla::DebugOnly<mozilla::LauncherVoidResult> reflectResult =
launcherRegInfo.ReflectTelemetryPrefToRegistry(prefVal);
MOZ_ASSERT(reflectResult.inspect().isOk());
}
static void SetupLauncherProcessPref() {
if (gLauncherProcessState) {
// We've already successfully run
return;
}
mozilla::LauncherRegistryInfo launcherRegInfo;
mozilla::LauncherResult<mozilla::LauncherRegistryInfo::EnabledState>
enabledState = launcherRegInfo.IsEnabled();
if (enabledState.isOk()) {
gLauncherProcessState = Some(enabledState.unwrap());
CrashReporter::RecordAnnotationU32(
CrashReporter::Annotation::LauncherProcessState,
static_cast<uint32_t>(enabledState.unwrap()));
// Reflect the launcher process registry state into user prefs
Preferences::SetBool(
PREF_WIN_LAUNCHER_PROCESS_ENABLED,
enabledState.unwrap() !=
mozilla::LauncherRegistryInfo::EnabledState::ForceDisabled);
}
mozilla::DebugOnly<mozilla::LauncherVoidResult> reflectResult =
launcherRegInfo.ReflectTelemetryPrefToRegistry(
Preferences::GetBool(kPrefHealthReportUploadEnabled, true));
MOZ_ASSERT(reflectResult.inspect().isOk());
Preferences::RegisterCallback(&OnLauncherPrefChanged,
PREF_WIN_LAUNCHER_PROCESS_ENABLED);
Preferences::RegisterCallback(&OnLauncherTelemetryPrefChanged,
kPrefHealthReportUploadEnabled);
}
# endif // defined(MOZ_LAUNCHER_PROCESS)
# if defined(MOZ_DEFAULT_BROWSER_AGENT)
# define DEFAULT_BROWSER_AGENT_KEY_NAME \
"SOFTWARE\\" MOZ_APP_VENDOR "\\" MOZ_APP_NAME "\\Default Browser Agent"
static nsresult PrependRegistryValueName(nsAutoString& aValueName) {
nsresult rv;
nsCOMPtr<nsIFile> binaryPath;
rv = XRE_GetBinaryPath(getter_AddRefs(binaryPath));
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIFile> binaryDir;
rv = binaryPath->GetParent(getter_AddRefs(binaryDir));
NS_ENSURE_SUCCESS(rv, rv);
nsAutoString prefix;
rv = binaryDir->GetPath(prefix);
NS_ENSURE_SUCCESS(rv, rv);
prefix.AppendLiteral("|");
aValueName.Insert(prefix, 0);
return NS_OK;
}
static void OnDefaultAgentTelemetryPrefChanged(const char* aPref, void* aData) {
nsresult rv;
nsAutoString valueName;
if (strcmp(aPref, kPrefHealthReportUploadEnabled) == 0) {
valueName.AssignLiteral("DisableTelemetry");
} else if (strcmp(aPref, kPrefDefaultAgentEnabled) == 0) {
valueName.AssignLiteral("DisableDefaultBrowserAgent");
} else {
return;
}
rv = PrependRegistryValueName(valueName);
NS_ENSURE_SUCCESS_VOID(rv);
nsCOMPtr<nsIWindowsRegKey> regKey =
do_CreateInstance("@mozilla.org/windows-registry-key;1", &rv);
NS_ENSURE_SUCCESS_VOID(rv);
nsAutoString keyName;
keyName.AppendLiteral(DEFAULT_BROWSER_AGENT_KEY_NAME);
rv = regKey->Create(nsIWindowsRegKey::ROOT_KEY_CURRENT_USER, keyName,
nsIWindowsRegKey::ACCESS_WRITE);
bool prefVal = Preferences::GetBool(aPref, true);
// We're recording whether the pref is *disabled*, so invert the value.
rv = regKey->WriteIntValue(valueName, prefVal ? 0 : 1);
NS_ENSURE_SUCCESS_VOID(rv);
}
static void OnSetDefaultBrowserUserChoicePrefChanged(const char* aPref,
void* aData) {
nsresult rv;
if (strcmp(aPref, kPrefSetDefaultBrowserUserChoicePref) != 0) {
return;
}
nsAutoString valueName;
valueName.AssignLiteral("SetDefaultBrowserUserChoice");
rv = PrependRegistryValueName(valueName);
NS_ENSURE_SUCCESS_VOID(rv);
nsCOMPtr<nsIWindowsRegKey> regKey =
do_CreateInstance("@mozilla.org/windows-registry-key;1", &rv);
NS_ENSURE_SUCCESS_VOID(rv);
nsAutoString keyName;
keyName.AppendLiteral(DEFAULT_BROWSER_AGENT_KEY_NAME);
rv = regKey->Create(nsIWindowsRegKey::ROOT_KEY_CURRENT_USER, keyName,
nsIWindowsRegKey::ACCESS_WRITE);
bool prefVal = Preferences::GetBool(aPref, true);
rv = regKey->WriteIntValue(valueName, prefVal);
NS_ENSURE_SUCCESS_VOID(rv);
}
static void OnDefaultAgentRemoteSettingsPrefChanged(const char* aPref,
void* aData) {
nsresult rv;
nsAutoString valueName;
if (strcmp(aPref, kPrefServicesSettingsServer) == 0) {
valueName.AssignLiteral("ServicesSettingsServer");
} else {
return;
}
rv = PrependRegistryValueName(valueName);
NS_ENSURE_SUCCESS_VOID(rv);
nsCOMPtr<nsIWindowsRegKey> regKey =
do_CreateInstance("@mozilla.org/windows-registry-key;1", &rv);
NS_ENSURE_SUCCESS_VOID(rv);
nsAutoString keyName;
keyName.AppendLiteral(DEFAULT_BROWSER_AGENT_KEY_NAME);
rv = regKey->Create(nsIWindowsRegKey::ROOT_KEY_CURRENT_USER, keyName,
nsIWindowsRegKey::ACCESS_WRITE);
NS_ENSURE_SUCCESS_VOID(rv);
nsAutoString prefVal;
rv = Preferences::GetString(aPref, prefVal);
if (NS_FAILED(rv)) {
return;
}
if (prefVal.IsEmpty()) {
rv = regKey->RemoveValue(valueName);
} else {
rv = regKey->WriteStringValue(valueName, prefVal);
}
NS_ENSURE_SUCCESS_VOID(rv);
}
static void SetDefaultAgentLastRunTime() {
nsresult rv;
nsAutoString valueName;
valueName.AppendLiteral("AppLastRunTime");
rv = PrependRegistryValueName(valueName);
NS_ENSURE_SUCCESS_VOID(rv);
nsCOMPtr<nsIWindowsRegKey> regKey =
do_CreateInstance("@mozilla.org/windows-registry-key;1", &rv);
NS_ENSURE_SUCCESS_VOID(rv);
nsAutoString keyName;
keyName.AppendLiteral(DEFAULT_BROWSER_AGENT_KEY_NAME);
rv = regKey->Create(nsIWindowsRegKey::ROOT_KEY_CURRENT_USER, keyName,
nsIWindowsRegKey::ACCESS_WRITE);
NS_ENSURE_SUCCESS_VOID(rv);
FILETIME fileTime;
GetSystemTimeAsFileTime(&fileTime);
ULARGE_INTEGER integerTime;
integerTime.u.LowPart = fileTime.dwLowDateTime;
integerTime.u.HighPart = fileTime.dwHighDateTime;
rv = regKey->WriteInt64Value(valueName, integerTime.QuadPart);
NS_ENSURE_SUCCESS_VOID(rv);
}
# endif // defined(MOZ_DEFAULT_BROWSER_AGENT)
#endif // XP_WIN
void UnlockProfile() {
if (gProfileLock) {
gProfileLock->Unlock();
}
}
nsresult LaunchChild(bool aBlankCommandLine, bool aTryExec) {
// Restart this process by exec'ing it into the current process
// if supported by the platform. Otherwise, use NSPR.
if (aBlankCommandLine) {
gRestartArgc = 1;
gRestartArgv[gRestartArgc] = nullptr;
}
SaveToEnv("MOZ_LAUNCHED_CHILD=1");
#if defined(MOZ_LAUNCHER_PROCESS)
SaveToEnv("MOZ_LAUNCHER_PROCESS=1");
#endif // defined(MOZ_LAUNCHER_PROCESS)
#if !defined(MOZ_WIDGET_ANDROID) // Android has separate restart code.
# if defined(XP_MACOSX)
InitializeMacApp();
CommandLineServiceMac::SetupMacCommandLine(gRestartArgc, gRestartArgv, true);
LaunchMacApp(gRestartArgc, gRestartArgv);
# else
nsCOMPtr<nsIFile> lf;
nsresult rv = XRE_GetBinaryPath(getter_AddRefs(lf));
if (NS_FAILED(rv)) return rv;
# if defined(XP_WIN)
nsAutoString exePath;
rv = lf->GetPath(exePath);
if (NS_FAILED(rv)) return rv;
HANDLE hProcess;
if (!WinLaunchChild(exePath.get(), gRestartArgc, gRestartArgv, nullptr,
&hProcess))
return NS_ERROR_FAILURE;
// Keep the current process around until the restarted process has created
// its message queue, to avoid the launched process's windows being forced
// into the background.
mozilla::WaitForInputIdle(hProcess);
::CloseHandle(hProcess);
# else
nsAutoCString exePath;
rv = lf->GetNativePath(exePath);
if (NS_FAILED(rv)) return rv;
# if defined(XP_UNIX)
if (aTryExec) {
execv(exePath.get(), gRestartArgv);
// If execv returns we know it's because it failed.
return NS_ERROR_FAILURE;
}
# endif
if (PR_CreateProcessDetached(exePath.get(), gRestartArgv, nullptr, nullptr) ==
PR_FAILURE) {
return NS_ERROR_FAILURE;
}
// Note that we don't know if the child process starts okay, if it
// immediately returns non-zero then we may mask that by returning a zero
// exit status.
# endif // XP_WIN
# endif // XP_MACOSX
#endif // MOZ_WIDGET_ANDROID
return NS_ERROR_LAUNCHED_CHILD_PROCESS;
}
static const char kProfileProperties[] =
"chrome://mozapps/locale/profile/profileSelection.properties";
namespace {
/**
* This class, instead of a raw nsresult, should be the return type of any
* function called by SelectProfile that initializes XPCOM.
*/
class ReturnAbortOnError {
public:
MOZ_IMPLICIT ReturnAbortOnError(nsresult aRv) { mRv = ConvertRv(aRv); }
operator nsresult() { return mRv; }
private:
inline nsresult ConvertRv(nsresult aRv) {
if (NS_SUCCEEDED(aRv) || aRv == NS_ERROR_LAUNCHED_CHILD_PROCESS) {
return aRv;
}
#ifdef MOZ_BACKGROUNDTASKS
// A background task that fails to lock its profile will return
// NS_ERROR_UNEXPECTED and this will allow the task to exit with a
// non-zero exit code.
if (aRv == NS_ERROR_UNEXPECTED && BackgroundTasks::IsBackgroundTaskMode()) {
return aRv;
}
#endif
return NS_ERROR_ABORT;
}
nsresult mRv;
};
} // namespace
static nsresult ProfileMissingDialog(nsINativeAppSupport* aNative) {
#ifdef MOZ_WIDGET_ANDROID
// We cannot really do anything this early during initialization, so we just
// return as this is likely the effect of misconfiguration on the test side.
// Non-test code paths cannot set the profile folder, which is always the
// default one.
Output(true, "Could not find profile folder.\n");
return NS_ERROR_ABORT;
#else
# ifdef MOZ_BACKGROUNDTASKS
if (BackgroundTasks::IsBackgroundTaskMode()) {
// We should never get to this point in background task mode.
printf_stderr(
"Could not determine any profile running in backgroundtask mode!\n");
return NS_ERROR_ABORT;
}
# endif // MOZ_BACKGROUNDTASKS
if (gfxPlatform::IsHeadless()) {
// Nothing can dismiss a modal dialog in headless mode, so report the
// failure on stderr and exit instead of spinning in the event loop.
Output(true, "Could not find profile folder.\n");
return NS_ERROR_ABORT;
}
nsresult rv;
ScopedXPCOMStartup xpcom;
rv = xpcom.Initialize();
NS_ENSURE_SUCCESS(rv, rv);
rv = xpcom.SetWindowCreator(aNative);
NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
# ifdef XP_MACOSX
InitializeMacApp();
# endif
{ // extra scoping is needed so we release these components before xpcom
// shutdown
nsCOMPtr<nsIStringBundleService> sbs =
mozilla::components::StringBundle::Service();
NS_ENSURE_TRUE(sbs, NS_ERROR_FAILURE);
nsCOMPtr<nsIStringBundle> sb;
sbs->CreateBundle(kProfileProperties, getter_AddRefs(sb));
NS_ENSURE_TRUE_LOG(sb, NS_ERROR_FAILURE);
NS_ConvertUTF8toUTF16 appName(gAppData->name);
AutoTArray<nsString, 2> params = {appName, appName};
// profileMissing
nsAutoString missingMessage;
rv = sb->FormatStringFromName("profileMissing", params, missingMessage);
NS_ENSURE_SUCCESS(rv, NS_ERROR_ABORT);
nsAutoString missingTitle;
params.SetLength(1);
rv = sb->FormatStringFromName("profileMissingTitle", params, missingTitle);
NS_ENSURE_SUCCESS(rv, NS_ERROR_ABORT);
nsCOMPtr<nsIPromptService> ps(do_GetService(NS_PROMPTSERVICE_CONTRACTID));
NS_ENSURE_TRUE(ps, NS_ERROR_FAILURE);
ps->Alert(nullptr, missingTitle.get(), missingMessage.get());
return NS_ERROR_ABORT;
}
#endif // MOZ_WIDGET_ANDROID
}
// Shows the "encryption pref vs on-disk state" mismatch dialog and
// returns NS_ERROR_ABORT so the caller can exit cleanly. aMsgKey /
// aTitleKey select which of the two scenarios (encrypted-but-pref-off
// or pref-on-but-plaintext) is being reported.
static nsresult ProfileEncryptionMismatchDialog(const char* aMsgKey,
const char* aTitleKey,
nsINativeAppSupport* aNative) {
#ifdef MOZ_WIDGET_ANDROID
Output(true, "Profile encryption state does not match launching build.\n");
return NS_ERROR_ABORT;
#else
# ifdef MOZ_BACKGROUNDTASKS
if (BackgroundTasks::IsBackgroundTaskMode()) {
printf_stderr(
"Profile encryption state does not match launching build in "
"backgroundtask mode\n");
return NS_ERROR_ABORT;
}
# endif // MOZ_BACKGROUNDTASKS
if (gfxPlatform::IsHeadless()) {
// Nothing can dismiss a modal dialog in headless mode.
Output(true, "Profile encryption state does not match launching build.\n");
return NS_ERROR_ABORT;
}
nsresult rv;
ScopedXPCOMStartup xpcom;
rv = xpcom.Initialize();
NS_ENSURE_SUCCESS(rv, rv);
rv = xpcom.SetWindowCreator(aNative);
NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
# ifdef XP_MACOSX
InitializeMacApp();
# endif
{ // extra scoping is needed so we release these components before xpcom
// shutdown
nsCOMPtr<nsIStringBundleService> sbs =
mozilla::components::StringBundle::Service();
NS_ENSURE_TRUE(sbs, NS_ERROR_FAILURE);
nsCOMPtr<nsIStringBundle> sb;
sbs->CreateBundle(kProfileProperties, getter_AddRefs(sb));
NS_ENSURE_TRUE_LOG(sb, NS_ERROR_FAILURE);
NS_ConvertUTF8toUTF16 appName(gAppData->name);
AutoTArray<nsString, 3> params = {appName, appName, appName};
nsAutoString msg;
rv = sb->FormatStringFromName(aMsgKey, params, msg);
NS_ENSURE_SUCCESS(rv, NS_ERROR_ABORT);
nsAutoString title;
params.SetLength(1);
rv = sb->FormatStringFromName(aTitleKey, params, title);
NS_ENSURE_SUCCESS(rv, NS_ERROR_ABORT);
nsCOMPtr<nsIPromptService> ps(do_GetService(NS_PROMPTSERVICE_CONTRACTID));
NS_ENSURE_TRUE(ps, NS_ERROR_FAILURE);
ps->Alert(nullptr, title.get(), msg.get());
return NS_ERROR_ABORT;
}
#endif // MOZ_WIDGET_ANDROID
}
static ReturnAbortOnError ProfileLockedDialog(nsIFile* aProfileDir,
nsIFile* aProfileLocalDir,
nsIProfileUnlocker* aUnlocker,
nsINativeAppSupport* aNative,
nsIProfileLock** aResult) {
nsresult rv;
// We aren't going to start this instance so we can unblock other instances
// from starting up.
#if defined(MOZ_HAS_REMOTE)
gStartupLock = nullptr;
#endif
bool exists;
aProfileDir->Exists(&exists);
if (!exists) {
return ProfileMissingDialog(aNative);
}
ScopedXPCOMStartup xpcom;
rv = xpcom.Initialize();
NS_ENSURE_SUCCESS(rv, rv);
#if defined(MOZ_TELEMETRY_REPORTING)
// We cannot check if telemetry has been disabled by the user, yet.
// So, rely on the build time settings, instead.
mozilla::Telemetry::WriteFailedProfileLock(aProfileDir);
#endif
rv = xpcom.SetWindowCreator(aNative);
NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
#ifdef XP_MACOSX
InitializeMacApp();
#endif
{ // extra scoping is needed so we release these components before xpcom
// shutdown
nsCOMPtr<nsIStringBundleService> sbs =
mozilla::components::StringBundle::Service();
NS_ENSURE_TRUE(sbs, NS_ERROR_FAILURE);
nsCOMPtr<nsIStringBundle> sb;
sbs->CreateBundle(kProfileProperties, getter_AddRefs(sb));
NS_ENSURE_TRUE_LOG(sbs, NS_ERROR_FAILURE);
NS_ConvertUTF8toUTF16 appName(gAppData->name);
AutoTArray<nsString, 3> params = {appName, appName, appName};
nsAutoString killMessage;
#ifndef XP_MACOSX
rv = sb->FormatStringFromName(
aUnlocker ? "restartMessageUnlocker" : "restartMessageNoUnlocker2",
params, killMessage);
#else
rv = sb->FormatStringFromName(
aUnlocker ? "restartMessageUnlockerMac" : "restartMessageNoUnlockerMac",
params, killMessage);
#endif
NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
params.SetLength(1);
nsAutoString killTitle;
rv = sb->FormatStringFromName("restartTitle", params, killTitle);
NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
#ifdef MOZ_BACKGROUNDTASKS
if (BackgroundTasks::IsBackgroundTaskMode()) {
// This error is handled specially to exit with a non-zero exit code.
printf_stderr("%s\n", NS_LossyConvertUTF16toASCII(killMessage).get());
return NS_ERROR_UNEXPECTED;
}
#endif
if (gfxPlatform::IsHeadless()) {
// TODO: make a way to turn off all dialogs when headless.
Output(true, "%s\n", NS_LossyConvertUTF16toASCII(killMessage).get());
return NS_ERROR_FAILURE;
}
nsCOMPtr<nsIPromptService> ps(do_GetService(NS_PROMPTSERVICE_CONTRACTID));
NS_ENSURE_TRUE(ps, NS_ERROR_FAILURE);
if (aUnlocker) {
int32_t button;
#ifdef MOZ_WIDGET_ANDROID
// On Android we always kill the process if the lock is still being held
button = 0;
#else
const uint32_t flags = (nsIPromptService::BUTTON_TITLE_IS_STRING *
nsIPromptService::BUTTON_POS_0) +
(nsIPromptService::BUTTON_TITLE_CANCEL *
nsIPromptService::BUTTON_POS_1);
bool checkState = false;
rv = ps->ConfirmEx(nullptr, killTitle.get(), killMessage.get(), flags,
killTitle.get(), nullptr, nullptr, nullptr,
&checkState, &button);
NS_ENSURE_SUCCESS_LOG(rv, rv);
#endif
if (button == 0) {
rv = aUnlocker->Unlock(nsIProfileUnlocker::FORCE_QUIT);
if (NS_FAILED(rv)) {
return rv;
}
SaveFileToEnv("XRE_PROFILE_PATH", aProfileDir);
SaveFileToEnv("XRE_PROFILE_LOCAL_PATH", aProfileLocalDir);
return LaunchChild(false, true);
}
} else {
rv = ps->Alert(nullptr, killTitle.get(), killMessage.get());
NS_ENSURE_SUCCESS_LOG(rv, rv);
}
return NS_ERROR_ABORT;
}
}
MOZ_CAN_RUN_SCRIPT
static ReturnAbortOnError ShowProfileDialog(
nsIToolkitProfileService* aProfileSvc, nsINativeAppSupport* aNative,
const char* aDialogURL, const char* aTelemetryEnvVar) {
nsresult rv;
nsCOMPtr<nsIFile> profD, profLD;
bool offline = false;
int32_t dialogReturn;
// We aren't going to start this instance so we can unblock other instances
// from starting up.
#if defined(MOZ_HAS_REMOTE)
gStartupLock = nullptr;
#endif
{
ScopedXPCOMStartup xpcom;
rv = xpcom.Initialize();
NS_ENSURE_SUCCESS(rv, rv);
rv = xpcom.SetWindowCreator(aNative);
NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
#ifdef XP_MACOSX
InitializeMacApp();
CommandLineServiceMac::SetupMacCommandLine(gRestartArgc, gRestartArgv,
true);
#endif
{ // extra scoping is needed so we release these components before xpcom
// shutdown
nsCOMPtr<nsIWindowWatcher> windowWatcher(
do_GetService(NS_WINDOWWATCHER_CONTRACTID));
nsCOMPtr<nsIDialogParamBlock> ioParamBlock(
do_CreateInstance(NS_DIALOGPARAMBLOCK_CONTRACTID));
nsCOMPtr<nsIMutableArray> dlgArray(
do_CreateInstance(NS_ARRAY_CONTRACTID));
NS_ENSURE_TRUE(windowWatcher && ioParamBlock && dlgArray,
NS_ERROR_FAILURE);
ioParamBlock->SetObjects(dlgArray);
nsCOMPtr<nsIAppStartup> appStartup(components::AppStartup::Service());
NS_ENSURE_TRUE(appStartup, NS_ERROR_FAILURE);
nsAutoCString features("centerscreen,chrome,modal,titlebar");
// If we're launching a private browsing window make sure to set that
// feature for the Profile Manager window as well, so it groups correctly
// on the Windows taskbar.
if (CheckArgExists("private-window") == ARG_FOUND) {
features.AppendLiteral(",private");
}
nsCOMPtr<mozIDOMWindowProxy> newWindow;
rv = windowWatcher->OpenWindow(nullptr, nsDependentCString(aDialogURL),
u"_blank"_ns, features, ioParamBlock,
getter_AddRefs(newWindow));
NS_ENSURE_SUCCESS_LOG(rv, rv);
rv = ioParamBlock->GetInt(0, &dialogReturn);
if (NS_FAILED(rv) || dialogReturn == nsIToolkitProfileService::exit) {
return NS_ERROR_ABORT;
}
int32_t startOffline;
rv = ioParamBlock->GetInt(1, &startOffline);
offline = NS_SUCCEEDED(rv) && startOffline == 1;
rv = dlgArray->QueryElementAt(0, NS_GET_IID(nsIFile),
getter_AddRefs(profD));
NS_ENSURE_SUCCESS_LOG(rv, rv);
rv = dlgArray->QueryElementAt(1, NS_GET_IID(nsIFile),
getter_AddRefs(profLD));
NS_ENSURE_SUCCESS_LOG(rv, rv);
if (dialogReturn == nsIToolkitProfileService::launchWithProfile) {
int32_t newArguments;
rv = ioParamBlock->GetInt(2, &newArguments);
if (NS_SUCCEEDED(rv) && newArguments > 0) {
char** newArgv = (char**)realloc(
gRestartArgv, sizeof(char*) * (gRestartArgc + newArguments + 1));
NS_ENSURE_TRUE(newArgv, NS_ERROR_OUT_OF_MEMORY);
gRestartArgv = newArgv;
for (auto i = 0; i < newArguments; i++) {
char16_t* arg;
ioParamBlock->GetString(i, &arg);
gRestartArgv[gRestartArgc++] =
strdup(NS_ConvertUTF16toUTF8(nsDependentString(arg)).get());
}
gRestartArgv[gRestartArgc] = nullptr;
}
}
}
}
if (offline) {
SaveToEnv("XRE_START_OFFLINE=1");
}
// User requested that we restart back into the profile manager.
if (dialogReturn == nsIToolkitProfileService::restart) {
SaveToEnv("XRE_RESTART_TO_PROFILE_MANAGER=1");
} else {
MOZ_ASSERT(dialogReturn == nsIToolkitProfileService::launchWithProfile);
SaveFileToEnv("XRE_PROFILE_PATH", profD);
SaveFileToEnv("XRE_PROFILE_LOCAL_PATH", profLD);
}
SaveToEnv(aTelemetryEnvVar);
if (gRestartedByOS) {
// Re-add this argument when actually starting the application.
char** newArgv =
(char**)realloc(gRestartArgv, sizeof(char*) * (gRestartArgc + 2));
NS_ENSURE_TRUE(newArgv, NS_ERROR_OUT_OF_MEMORY);
gRestartArgv = newArgv;
gRestartArgv[gRestartArgc++] = const_cast<char*>("-os-restarted");
gRestartArgv[gRestartArgc] = nullptr;
}
return LaunchChild(false, true);
}
MOZ_CAN_RUN_SCRIPT
static ReturnAbortOnError ShowProfileManager(
nsIToolkitProfileService* aProfileSvc, nsINativeAppSupport* aNative) {
static const char kProfileManagerURL[] =
"chrome://mozapps/content/profile/profileSelection.xhtml";
static const char kTelemetryEnv[] = "XRE_RESTARTED_BY_PROFILE_MANAGER=1";
return ShowProfileDialog(aProfileSvc, aNative, kProfileManagerURL,
kTelemetryEnv);
}
MOZ_CAN_RUN_SCRIPT
static ReturnAbortOnError ShowProfileSelector(
nsIToolkitProfileService* aProfileSvc, nsINativeAppSupport* aNative) {
static const char kProfileSelectorURL[] = "about:profilemanager";
static const char kTelemetryEnv[] = "XRE_RESTARTED_BY_PROFILE_SELECTOR=1";
return ShowProfileDialog(aProfileSvc, aNative, kProfileSelectorURL,
kTelemetryEnv);
}
// Both profile dialogs relaunch Firefox to start the chosen profile, and macOS
// hands an ASWebAuthenticationSession request to the process it launched rather
// than to the relaunched one, so showing a dialog would drop the request.
static bool ShouldSkipProfileDialogForWebAuth() {
#if defined(XP_MACOSX)
return WasLaunchedByAuthenticationServices();
#else
return false;
#endif
}
static bool gDoMigration = false;
static bool gDoProfileReset = false;
constinit static nsCOMPtr<nsIToolkitProfile> gResetOldProfile;
static nsresult LockProfile(nsINativeAppSupport* aNative, nsIFile* aRootDir,
nsIFile* aLocalDir, nsIToolkitProfile* aProfile,
nsIProfileLock** aResult) {
// If you close Firefox and very quickly reopen it, the old Firefox may
// still be closing down. Rather than immediately showing the
// "Firefox is running but is not responding" message, we spend a few
// seconds retrying first.
static const int kLockRetrySeconds = 5;
static const int kLockRetrySleepMS = 100;
nsresult rv;
nsCOMPtr<nsIProfileUnlocker> unlocker;
const TimeStamp start = TimeStamp::Now();
do {
if (aProfile) {
rv = aProfile->Lock(getter_AddRefs(unlocker), aResult);
} else {
rv = NS_LockProfilePath(aRootDir, aLocalDir, getter_AddRefs(unlocker),
aResult);
}
if (NS_SUCCEEDED(rv)) {
StartupTimeline::Record(StartupTimeline::AFTER_PROFILE_LOCKED);
return NS_OK;
}
PR_Sleep(kLockRetrySleepMS);
} while (TimeStamp::Now() - start <
TimeDuration::FromSeconds(kLockRetrySeconds));
return ProfileLockedDialog(aRootDir, aLocalDir, unlocker, aNative, aResult);
}
// Pick a profile. We need to end up with a profile root dir, local dir and
// potentially an nsIToolkitProfile instance.
//
// 1) check for --profile <path>
// 2) check for -P <name>
// 3) check for --ProfileManager
// 4) use the default profile, if there is one
// 5) if there are *no* profiles, set up profile-migration
// 6) display the profile-manager UI
MOZ_CAN_RUN_SCRIPT
static nsresult SelectProfile(nsToolkitProfileService* aProfileSvc,
nsINativeAppSupport* aNative, nsIFile** aRootDir,
nsIFile** aLocalDir, nsIToolkitProfile** aProfile,
bool* aWasDefaultSelection) {
StartupTimeline::Record(StartupTimeline::SELECT_PROFILE);
nsresult rv;
if (EnvHasValue("MOZ_RESET_PROFILE_RESTART")) {
gDoProfileReset = true;
gDoMigration = true;
}
// reset-profile and migration args need to be checked before any profiles are
// chosen below.
ArgResult ar = CheckArg("reset-profile");
if (ar == ARG_FOUND) {
gDoProfileReset = true;
}
ar = CheckArg("migration");
if (ar == ARG_FOUND) {
gDoMigration = true;
}
#if defined(XP_WIN)
// This arg is only used to indicate to telemetry that a profile refresh
// (reset+migration) was requested from the uninstaller, pass this along
// via an environment variable for simplicity.
ar = CheckArg("uninstaller-profile-refresh");
if (ar == ARG_FOUND) {
SaveToEnv("MOZ_UNINSTALLER_PROFILE_REFRESH=1");
}
#endif
if (EnvHasValue("XRE_RESTART_TO_PROFILE_MANAGER")) {
return ShowProfileManager(aProfileSvc, aNative);
}
// Ask the profile manager to select the profile directories to use.
bool didCreate = false;
rv = aProfileSvc->SelectStartupProfile(&gArgc, gArgv, gDoProfileReset,
aRootDir, aLocalDir, aProfile,
&didCreate, aWasDefaultSelection);
if (rv == NS_ERROR_SHOW_PROFILE_MANAGER) {
return ShowProfileManager(aProfileSvc, aNative);
}
NS_ENSURE_SUCCESS(rv, rv);
if (didCreate) {
// For a fresh install, we would like to let users decide
// to do profile migration on their own later after using.
gDoProfileReset = false;
gDoMigration = false;
}
if (gDoProfileReset && !*aProfile) {
NS_WARNING("Profile reset is only supported for named profiles.");
return NS_ERROR_ABORT;
}
// Block reset without migration for selectable profiles.
if (gDoProfileReset && !gDoMigration && *aProfile) {
nsCString storeID;
(*aProfile)->GetStoreID(storeID);
if (!storeID.IsVoid()) {
NS_WARNING("Selectable profiles cannot be reset without migration.");
return NS_ERROR_ABORT;
}
}
// No profile could be found. This generally shouldn't happen, a new profile
// should be created in all cases except for profile reset which is covered
// above, but just in case...
if (!*aRootDir) {
NS_WARNING("Failed to select or create profile.");
return NS_ERROR_ABORT;
}
return NS_OK;
}
#ifdef MOZ_BLOCK_PROFILE_DOWNGRADE
struct FileWriteFunc final : public JSONWriteFunc {
FILE* mFile;
explicit FileWriteFunc(FILE* aFile) : mFile(aFile) {}
void Write(const Span<const char>& aStr) final {
fprintf(mFile, "%.*s", int(aStr.size()), aStr.data());
}
};
// Parses a JSON string and returns the raw install_timestamp value, or
// Nothing() if the JSON is malformed or lacks the property.
static mozilla::Maybe<uint64_t> ParseInstallTimestamp(
std::string_view aUtf8Json) {
Json::Value root;
Json::Reader reader;
if (!reader.parse(aUtf8Json.data(), aUtf8Json.data() + aUtf8Json.size(),
root) ||
!root.isMember("install_timestamp")) {
return mozilla::Nothing();
}
std::string tsStr = root["install_timestamp"].asString();
char* end = nullptr;
uint64_t val = strtoull(tsStr.c_str(), &end, 10);
if (!end || *end != '\0') {
return mozilla::Nothing();
}
return mozilla::Some(val);
}
# ifndef XP_MACOSX
// Reads aJsonFile and returns the raw install_timestamp value, or Nothing() if
// the file is absent, unreadable, or lacks the property.
static mozilla::Maybe<uint64_t> ReadInstallTimestamp(nsIFile* aJsonFile,
bool aIsUTF16LE) {
FILE* raw = nullptr;
if (NS_FAILED(aJsonFile->OpenANSIFileDesc("rb", &raw)) || !raw) {
return mozilla::Nothing();
}
ScopedCloseFile f(raw);
fseek(f.get(), 0, SEEK_END);
auto len = ftell(f.get());
if (len <= 0) {
return mozilla::Nothing();
}
rewind(f.get());
auto buf = MakeUnique<uint8_t[]>(len);
if (fread(buf.get(), 1, len, f.get()) != (size_t)len) {
return mozilla::Nothing();
}
nsAutoCString converted;
std::string_view utf8View;
if (aIsUTF16LE) {
# if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
const char16_t* chars = reinterpret_cast<const char16_t*>(buf.get());
uint32_t charLen = len / 2;
CopyUTF16toUTF8(Span(chars, charLen), converted);
utf8View = std::string_view(converted.get(), converted.Length());
# else
MOZ_ASSERT_UNREACHABLE(
"UTF-16LE reading not supported on big-endian architectures");
return mozilla::Nothing();
# endif
} else {
utf8View = std::string_view(reinterpret_cast<const char*>(buf.get()), len);
}
return ParseInstallTimestamp(utf8View);
}
# endif // !XP_MACOSX
Maybe<mozilla::PathString> GenerateDowngradeTelemetry(
const nsACString& aPingId, const nsCString& aLastVersion, bool aHasSync,
int32_t aButton, const nsACString& aChannel,
const nsACString& aProfileSelectionReason,
mozilla::Maybe<PRTime> aReplacedLockTime, bool aIsDifferentInstall) {
nsCOMPtr<nsIPrefService> prefSvc =
do_GetService("@mozilla.org/preferences-service;1");
NS_ENSURE_TRUE(prefSvc, Nothing());
nsCOMPtr<nsIPrefBranch> prefBranch = do_QueryInterface(prefSvc);
NS_ENSURE_TRUE(prefBranch, Nothing());
nsCString clientId;
nsresult rv =
prefBranch->GetCharPref("toolkit.telemetry.cachedClientID", clientId);
NS_ENSURE_SUCCESS(rv, Nothing());
nsCString profileGroupId;
rv = prefBranch->GetCharPref("toolkit.telemetry.cachedProfileGroupID",
profileGroupId);
NS_ENSURE_SUCCESS(rv, Nothing());
nsCString arch("null");
nsCOMPtr<nsIPropertyBag2> sysInfo =
do_GetService("@mozilla.org/system-info;1");
NS_ENSURE_TRUE(sysInfo, Nothing());
sysInfo->GetPropertyAsACString(u"arch"_ns, arch);
bool isMSIX = false;
# ifdef XP_WIN
rv = sysInfo->GetPropertyAsBool(u"hasWinPackageId"_ns, &isMSIX);
if (rv != NS_OK) {
// Don't early return.
NS_ERROR("Failed to get property: hasWinPackageId");
}
# endif
mozilla::Maybe<PRTime> maybeInstallTime;
mozilla::Maybe<PRTime> maybeUpdateTime;
nsCOMPtr<nsIFile> greDir;
if (NS_SUCCEEDED(
NS_GetSpecialDirectory(NS_GRE_DIR, getter_AddRefs(greDir)))) {
# ifdef XP_WIN
// installation_telemetry.json uses a Windows FILETIME (100ns intervals
// since Jan 1, 1601 UTC).
nsCOMPtr<nsIFile> installTelemetry;
if (NS_SUCCEEDED(greDir->Clone(getter_AddRefs(installTelemetry))) &&
NS_SUCCEEDED(
installTelemetry->Append(u"installation_telemetry.json"_ns))) {
if (auto filetime =
ReadInstallTimestamp(installTelemetry, /* aIsUTF16LE */ true)) {
constexpr uint64_t kEpochOffset = 116444736000000000ULL;
if (*filetime > kEpochOffset) {
// The offset converts to an epoch of 1970, divide by 10 to convert
// from 100ns to usec
maybeInstallTime =
mozilla::Some(PRTime((*filetime - kEpochOffset) / 10));
}
}
}
# endif
# ifdef XP_MACOSX
// GreD is <app>.app/Contents/Resources; go up two levels to the .app
// bundle directory where the updater stores the xattr.
maybeUpdateTime = [&]() -> mozilla::Maybe<PRTime> {
nsCOMPtr<nsIFile> contentsDir;
if (NS_FAILED(greDir->GetParent(getter_AddRefs(contentsDir))) ||
!contentsDir) {
return mozilla::Nothing();
}
nsCOMPtr<nsIFile> appBundle;
if (NS_FAILED(contentsDir->GetParent(getter_AddRefs(appBundle))) ||
!appBundle) {
return mozilla::Nothing();
}
nsCOMPtr<nsILocalFileMac> macFile = do_QueryInterface(appBundle);
if (!macFile) {
return mozilla::Nothing();
}
nsTArray<uint8_t> attrValue;
if (NS_FAILED(macFile->GetXAttr("org.mozilla.update-telemetry"_ns,
attrValue))) {
return mozilla::Nothing();
}
auto msTime = ParseInstallTimestamp(
std::string_view(reinterpret_cast<const char*>(attrValue.Elements()),
attrValue.Length()));
if (!msTime) {
return mozilla::Nothing();
}
return mozilla::Some(PRTime(int64_t(*msTime) * PR_USEC_PER_MSEC));
}();
# else
// update_telemetry.json uses a Unix timestamp in milliseconds.
nsCOMPtr<nsIFile> updateTelemetry;
if (NS_SUCCEEDED(greDir->Clone(getter_AddRefs(updateTelemetry))) &&
NS_SUCCEEDED(updateTelemetry->Append(u"update_telemetry.json"_ns))) {
if (auto msTime =
ReadInstallTimestamp(updateTelemetry, /* aIsUTF16LE */ false)) {
maybeUpdateTime =
mozilla::Some(PRTime(int64_t(*msTime) * PR_USEC_PER_MSEC));
}
}
# endif
}
PRTime nowUsec = PR_Now();
time_t nowTime = time_t(nowUsec / PR_USEC_PER_SEC);
char date[sizeof "YYYY-MM-DDThh:mm:ss.000Z"];
strftime(date, sizeof date, "%FT%T.000Z", gmtime(&nowTime));
constexpr auto pingType = "downgrade"_ns;
int32_t pos = aLastVersion.Find("_");
if (pos == kNotFound) {
return Nothing();
}
const nsDependentCSubstring lastVersion = Substring(aLastVersion, 0, pos);
const nsDependentCSubstring lastBuildId =
Substring(aLastVersion, pos + 1, 14);
nsCOMPtr<nsIFile> pingFile;
rv = NS_GetSpecialDirectory(XRE_USER_APP_DATA_DIR, getter_AddRefs(pingFile));
NS_ENSURE_SUCCESS(rv, Nothing());
rv = pingFile->Append(u"Pending Pings"_ns);
NS_ENSURE_SUCCESS(rv, Nothing());
rv = pingFile->Create(nsIFile::DIRECTORY_TYPE, 0755);
if (NS_FAILED(rv) && rv != NS_ERROR_FILE_ALREADY_EXISTS) {
return Nothing();
}
rv = pingFile->Append(NS_ConvertUTF8toUTF16(aPingId));
NS_ENSURE_SUCCESS(rv, Nothing());
FILE* file;
rv = pingFile->OpenANSIFileDesc("w", &file);
NS_ENSURE_SUCCESS(rv, Nothing());
JSONWriter w(MakeUnique<FileWriteFunc>(file));
w.Start();
{
w.StringProperty("type",
Span<const char>(pingType.Data(), pingType.Length()));
w.StringProperty("id", PromiseFlatCString(aPingId));
w.StringProperty("creationDate", MakeStringSpan(date));
w.IntProperty("version", TELEMETRY_PING_FORMAT_VERSION);
w.StringProperty("clientId", clientId);
w.StringProperty("profileGroupId", profileGroupId);
w.StartObjectProperty("application");
{
w.StringProperty("architecture", arch);
w.StringProperty(
"buildId",
MakeStringSpan(static_cast<const char*>(gAppData->buildID)));
w.StringProperty(
"name", MakeStringSpan(static_cast<const char*>(gAppData->name)));
w.StringProperty(
"version",
MakeStringSpan(static_cast<const char*>(gAppData->version)));
w.StringProperty("displayVersion",
MOZ_STRINGIFY(MOZ_APP_VERSION_DISPLAY));
w.StringProperty(
"vendor", MakeStringSpan(static_cast<const char*>(gAppData->vendor)));
w.StringProperty("platformVersion", gToolkitVersion);
# ifdef TARGET_XPCOM_ABI
w.StringProperty("xpcomAbi", TARGET_XPCOM_ABI);
# else
w.StringProperty("xpcomAbi", "unknown");
# endif
w.StringProperty("channel", PromiseFlatCString(aChannel));
}
w.EndObject();
w.StartObjectProperty("payload");
{
w.StringProperty("lastVersion", PromiseFlatCString(lastVersion));
w.StringProperty("lastBuildId", PromiseFlatCString(lastBuildId));
w.BoolProperty("hasSync", aHasSync);
w.IntProperty("button", aButton);
w.BoolProperty("isMSIX", isMSIX);
w.StringProperty("profileSelectionReason",
PromiseFlatCString(aProfileSelectionReason));
w.BoolProperty("isDifferentInstall", aIsDifferentInstall);
if (aReplacedLockTime) {
// aReplacedLockTime is in milliseconds.
PRTime lockTimeUsec = *aReplacedLockTime * PR_USEC_PER_MSEC;
constexpr int64_t kUsecsPerDay =
int64_t(PR_USEC_PER_SEC) * 60 * 60 * 24;
int64_t elapsedUsec = nowUsec - lockTimeUsec;
if (elapsedUsec >= 0) {
w.IntProperty("daysSinceLock", elapsedUsec / kUsecsPerDay);
}
if (maybeInstallTime) {
w.BoolProperty("isNewInstall", *maybeInstallTime > lockTimeUsec);
}
if (maybeUpdateTime) {
w.BoolProperty("isNewUpdate", *maybeUpdateTime > lockTimeUsec);
}
}
}
w.EndObject();
}
w.End();
fclose(file);
return Some(pingFile->NativePath());
}
bool BuildDowngradePingUrl(const nsACString& aPingId,
const nsACString& aChannel, nsACString& aUrlOut) {
nsCOMPtr<nsIPrefService> prefSvc =
do_GetService("@mozilla.org/preferences-service;1");
NS_ENSURE_TRUE(prefSvc, false);
nsCOMPtr<nsIPrefBranch> prefBranch = do_QueryInterface(prefSvc);
NS_ENSURE_TRUE(prefBranch, false);
nsCString server;
nsresult rv = prefBranch->GetCharPref("toolkit.telemetry.server", server);
NS_ENSURE_SUCCESS(rv, false);
aUrlOut = nsPrintfCString(
"%s/submit/telemetry/%s/%s/%s/%s/%s/%s?v=%d", server.get(),
PromiseFlatCString(aPingId).get(), "downgrade",
(const char*)gAppData->name, (const char*)gAppData->version,
PromiseFlatCString(aChannel).get(), (const char*)gAppData->buildID,
TELEMETRY_PING_FORMAT_VERSION);
return true;
}
static void SubmitDowngradeTelemetry(const nsACString& aProfileSelectionReason,
mozilla::Maybe<PRTime> aReplacedLockTime,
const nsCString& aLastVersion,
bool aHasSync, int32_t aButton,
bool aIsDifferentInstall) {
nsCOMPtr<nsIPrefService> prefSvc =
do_GetService("@mozilla.org/preferences-service;1");
NS_ENSURE_TRUE_VOID(prefSvc);
nsCOMPtr<nsIPrefBranch> prefBranch = do_QueryInterface(prefSvc);
NS_ENSURE_TRUE_VOID(prefBranch);
bool enabled;
nsresult rv =
prefBranch->GetBoolPref(kPrefHealthReportUploadEnabled, &enabled);
NS_ENSURE_SUCCESS_VOID(rv);
if (!enabled) {
return;
}
nsCOMPtr<nsIFile> pingSender;
rv = NS_GetSpecialDirectory(NS_GRE_BIN_DIR, getter_AddRefs(pingSender));
NS_ENSURE_SUCCESS_VOID(rv);
# ifdef XP_WIN
pingSender->Append(u"pingsender.exe"_ns);
# else
pingSender->Append(u"pingsender"_ns);
# endif
bool exists;
rv = pingSender->Exists(&exists);
NS_ENSURE_SUCCESS_VOID(rv);
if (!exists) {
return;
}
rv = prefSvc->GetDefaultBranch(nullptr, getter_AddRefs(prefBranch));
NS_ENSURE_SUCCESS_VOID(rv);
nsCString channel("default");
rv = prefBranch->GetCharPref("app.update.channel", channel);
NS_ENSURE_SUCCESS_VOID(rv);
nsID uuid;
rv = nsID::GenerateUUIDInPlace(uuid);
NS_ENSURE_SUCCESS_VOID(rv);
NSID_TrimBracketsASCII pingId(uuid);
nsCString url;
if (!BuildDowngradePingUrl(pingId, channel, url)) {
return;
}
Maybe<PathString> filePath = GenerateDowngradeTelemetry(
pingId, aLastVersion, aHasSync, aButton, channel, aProfileSelectionReason,
aReplacedLockTime, aIsDifferentInstall);
if (!filePath) {
return;
}
const filesystem::Path::value_type* args[2];
# ifdef XP_WIN
nsString urlw = NS_ConvertUTF8toUTF16(url);
args[0] = urlw.get();
# else
args[0] = url.get();
# endif
args[1] = filePath->get();
nsCOMPtr<nsIProcess> process =
do_CreateInstance("@mozilla.org/process/util;1");
NS_ENSURE_TRUE_VOID(process);
process->Init(pingSender);
process->SetStartHidden(true);
process->SetNoShell(true);
# ifdef XP_WIN
process->Runw(false, args, 2);
# else
process->Run(false, args, 2);
# endif
}
static const char kProfileDowngradeURL[] =
"chrome://mozapps/content/profile/profileDowngrade.xhtml";
MOZ_CAN_RUN_SCRIPT
static ReturnAbortOnError HandleDetectedDowngrade(
nsIFile* aProfileDir, nsINativeAppSupport* aNative,
nsToolkitProfileService* aProfileSvc, nsIProfileLock* aProfileLock,
const nsCString& aLastVersion, bool aIsDifferentInstall) {
int32_t result = 0;
nsresult rv;
{
if (gfxPlatform::IsHeadless()) {
// TODO: make a way to turn off all dialogs when headless.
Output(true,
"This profile was last used with a newer version of this "
"application. Please create a new profile.\n");
return NS_ERROR_ABORT;
}
ScopedXPCOMStartup xpcom;
rv = xpcom.Initialize();
NS_ENSURE_SUCCESS(rv, rv);
rv = xpcom.SetWindowCreator(aNative);
NS_ENSURE_SUCCESS(rv, rv);
# ifdef XP_MACOSX
InitializeMacApp();
# endif
{ // extra scoping is needed so we release these components before xpcom
// shutdown
bool hasSync = false;
nsCOMPtr<nsIPrefService> prefSvc =
do_GetService("@mozilla.org/preferences-service;1");
NS_ENSURE_TRUE(prefSvc, rv);
nsCOMPtr<nsIFile> prefsFile;
rv = aProfileDir->Clone(getter_AddRefs(prefsFile));
NS_ENSURE_SUCCESS(rv, rv);
rv = prefsFile->Append(u"prefs.js"_ns);
NS_ENSURE_SUCCESS(rv, rv);
rv = prefSvc->ReadUserPrefsFromFile(prefsFile);
if (NS_SUCCEEDED(rv)) {
nsCOMPtr<nsIPrefBranch> prefBranch = do_QueryInterface(prefSvc);
rv = prefBranch->PrefHasUserValue("services.sync.username", &hasSync);
NS_ENSURE_SUCCESS(rv, rv);
}
nsCOMPtr<nsIWindowWatcher> windowWatcher =
do_GetService(NS_WINDOWWATCHER_CONTRACTID);
NS_ENSURE_TRUE(windowWatcher, NS_ERROR_ABORT);
nsCOMPtr<nsIAppStartup> appStartup(components::AppStartup::Service());
NS_ENSURE_TRUE(appStartup, NS_ERROR_FAILURE);
nsCOMPtr<nsIDialogParamBlock> paramBlock =
do_CreateInstance(NS_DIALOGPARAMBLOCK_CONTRACTID);
NS_ENSURE_TRUE(paramBlock, NS_ERROR_ABORT);
uint8_t flags = 0;
if (hasSync) {
flags |= nsIToolkitProfileService::hasSync;
}
paramBlock->SetInt(0, flags);
nsAutoCString features("centerscreen,chrome,modal,titlebar");
// If we're launching a private browsing window make sure to set that
// feature for the Profile Manager window as well, so it groups correctly
// on the Windows taskbar.
if (CheckArgExists("private-window") == ARG_FOUND) {
features.AppendLiteral(",private");
}
nsCOMPtr<mozIDOMWindowProxy> newWindow;
rv = windowWatcher->OpenWindow(
nullptr, nsDependentCString(kProfileDowngradeURL), u"_blank"_ns,
features, paramBlock, getter_AddRefs(newWindow));
NS_ENSURE_SUCCESS(rv, rv);
paramBlock->GetInt(1, &result);
PRTime replacedLockTime = 0;
mozilla::Maybe<PRTime> maybeReplacedLockTime;
if (NS_SUCCEEDED(aProfileLock->GetReplacedLockTime(&replacedLockTime)) &&
replacedLockTime != 0) {
maybeReplacedLockTime = mozilla::Some(replacedLockTime);
}
SubmitDowngradeTelemetry(aProfileSvc->ProfileSelectionReason(),
maybeReplacedLockTime, aLastVersion, hasSync,
result, aIsDifferentInstall);
}
}
if (result == nsIToolkitProfileService::createNewProfile) {
// Create a new profile and start it.
nsCString profileName;
profileName.AssignLiteral("default");
# ifdef MOZ_DEDICATED_PROFILES
profileName.Append("-" MOZ_STRINGIFY(MOZ_UPDATE_CHANNEL));
# endif
nsCOMPtr<nsIToolkitProfile> newProfile;
rv = aProfileSvc->CreateUniqueProfile(nullptr, profileName, "downgrade"_ns,
getter_AddRefs(newProfile));
NS_ENSURE_SUCCESS(rv, rv);
rv = aProfileSvc->SetDefaultProfile(newProfile);
NS_ENSURE_SUCCESS(rv, rv);
rv = aProfileSvc->Flush();