/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* 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 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "mozilla/dom/ContentParent.h" #include "mozilla/dom/ContentChild.h" #include "mozilla/ipc/GeckoChildProcessHost.h" #include "mozilla/AppShutdown.h" #include "mozilla/ArrayUtils.h" #include "mozilla/Assertions.h" #include "mozilla/Attributes.h" #include "mozilla/Components.h" #include "mozilla/FilePreferences.h" #include "mozilla/ChaosMode.h" #include "mozilla/CmdLineAndEnvUtils.h" #include "mozilla/IOInterposer.h" #include "mozilla/ipc/UtilityProcessChild.h" #include "mozilla/Likely.h" #include "mozilla/MemoryChecking.h" #include "mozilla/Poison.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/ScopeExit.h" #include "mozilla/StaticPrefs_browser.h" #include "mozilla/StaticPrefs_fission.h" #include "mozilla/StaticPrefs_webgl.h" #include "mozilla/StaticPrefs_widget.h" #include "mozilla/Telemetry.h" #include "mozilla/Utf8.h" #include "mozilla/intl/LocaleService.h" #include "mozilla/JSONWriter.h" #include "mozilla/gfx/gfxVars.h" #include "mozilla/glean/GleanMetrics.h" #include "mozilla/glean/GleanPings.h" #include "mozilla/widget/TextRecognition.h" #include "BaseProfiler.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 MOZ_INSTRUMENT_EVENT_LOOP # include "EventTracer.h" #endif #ifdef XP_MACOSX # include "nsVersionComparator.h" # include "MacLaunchHelper.h" # include "MacApplicationDelegate.h" # include "MacAutoreleasePool.h" # include "MacRunFromDmgUtils.h" // these are needed for sysctl # include # include #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 #include "mozilla/Unused.h" #ifdef XP_WIN # include "nsIWinAppHelper.h" # include # include # include # include "cairo/cairo-features.h" # include "detect_win32k_conflicts.h" # include "mozilla/PreXULSkeletonUI.h" # include "mozilla/DllPrefetchExperimentRegistryInfo.h" # include "mozilla/WindowsBCryptInitialization.h" # include "mozilla/WindowsDllBlocklist.h" # include "mozilla/WindowsMsctfInitialization.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" # if defined(XP_WIN) # include "mozilla/a11y/Compatibility.h" # include "mozilla/a11y/Platform.h" # endif #endif #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 #include #ifdef XP_UNIX # include # include # include # include # include # include #endif #ifdef XP_WIN # include # include # 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 #endif #if defined(XP_MACOSX) # include #endif #ifdef DEBUG # include "mozilla/Logging.h" #endif #ifdef MOZ_JPROF # include "jprof.h" #endif #include "nsExceptionHandler.h" #include "nsICrashReporter.h" #include "nsIPrefService.h" #include "nsIMemoryInfoDumper.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 "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 extern uint32_t gRestartMode; 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"; static const char kPrefBrowserStartupBlankWindow[] = "browser.startup.blankWindow"; static const char kPrefPreXulSkeletonUI[] = "browser.startup.preXulSkeletonUI"; #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) static nsRemoteService* gRemoteService; bool gRestartWithoutRemote = false; #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; nsString gAbsoluteArgv0Path; #if defined(XP_WIN) nsString gProcessStartupShortcut; #endif #if defined(MOZ_WIDGET_GTK) # include # include "mozilla/WidgetUtilsGtk.h" # include # ifdef MOZ_WAYLAND # include # include "mozilla/widget/nsWaylandDisplay.h" # include "wayland-proxy.h" # endif # ifdef MOZ_X11 # include # endif /* MOZ_X11 */ #endif #if defined(MOZ_WAYLAND) std::unique_ptr gWaylandProxy; #endif #include "BinaryPath.h" #ifdef MOZ_LOGGING # include "mozilla/Logging.h" extern mozilla::LazyLogModule gWidgetWaylandLog; #endif /* MOZ_LOGGING */ #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**) = 0; bool RunningGTest() { return RunGTest; } } // namespace mozilla using namespace mozilla; using namespace mozilla::widget; using namespace mozilla::startup; using mozilla::Unused; using mozilla::dom::ContentChild; using mozilla::dom::ContentParent; using mozilla::dom::quota::QuotaManager; using mozilla::intl::LocaleService; using mozilla::scache::StartupCache; // 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. } // 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)); MessageBoxW(nullptr, wide_msg, L"XULRunner", 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 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 use "are non-local connections disabled" as a proxy for // "are we running some kind of automated test". It would be nicer to use // xpc::IsInAutomation(), but that depends on some prefs being set, which // they are not in (at least) gtests (where we can't) and xpcshell. // Long-term, hopefully we can make all tests e10s-friendly, // then we could remove this automation-only env variable. if (gBrowserTabsRemoteAutostart && xpc::AreNonLocalConnectionsDisabled()) { 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::MissingRemoteWebGL || 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 (gSafeMode) { return nsIXULRuntime::ContentWin32kLockdownState::DisabledBySafeMode; } 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; } { ConflictingMitigationStatus conflictingMitigationStatus = {}; if (!detect_win32k_conflicting_mitigations(&conflictingMitigationStatus)) { return nsIXULRuntime::ContentWin32kLockdownState:: IncompatibleMitigationPolicy; } if (conflictingMitigationStatus.caller_check || conflictingMitigationStatus.sim_exec || conflictingMitigationStatus.stack_pivot) { return nsIXULRuntime::ContentWin32kLockdownState:: IncompatibleMitigationPolicy; } } // Non-native theming is required as well if (!StaticPrefs::widget_non_native_theme_enabled()) { return nsIXULRuntime::ContentWin32kLockdownState::MissingNonNativeTheming; } // Win32k Lockdown requires Remote WebGL, but it may be disabled on // certain hardware or virtual machines. if (!gfx::gfxVars::AllowWebglOop() || !StaticPrefs::webgl_out_of_process()) { return nsIXULRuntime::ContentWin32kLockdownState::MissingRemoteWebGL; } // 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 observerService = mozilla::services::GetObserverService()) { nsCOMPtr 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 SessionHistoryInParent() { return FissionAutostart() || !StaticPrefs:: fission_disableSessionHistoryInParent_AtStartup_DoNotUseDirectly(); } bool SessionStorePlatformCollection() { return SessionHistoryInParent() && !StaticPrefs:: browser_sessionstore_disable_platform_collection_AtStartup_DoNotUseDirectly(); } bool BFCacheInParent() { return SessionHistoryInParent() && 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::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(GeckoProcessType_##enum_name), \ "GeckoProcessType in nsXULAppAPI.h not synchronized with " \ "nsIXULRuntime.idl"); #include "mozilla/GeckoProcessTypes.h" #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(); } else { aRemoteType = NOT_REMOTE_TYPE; } return NS_OK; } static nsCString gLastAppVersion; static nsCString gLastAppBuildID; 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 eFissionExperimentControl: aResult = "experimentControl"; break; case eFissionExperimentTreatment: aResult = "experimentTreatment"; break; 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; case eFissionEnabledByRollout: aResult = "enabledByRollout"; break; default: MOZ_ASSERT_UNREACHABLE("Unexpected enum value"); } return NS_OK; } NS_IMETHODIMP nsXULAppInfo::GetSessionHistoryInParent(bool* aResult) { *aResult = SessionHistoryInParent(); 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) { #if defined(ACCESSIBILITY) && defined(XP_WIN) if (!GetAccService()) { aInstantiator.Truncate(); return NS_OK; } nsAutoString ipClientInfo; a11y::Compatibility::GetHumanReadableConsumersStr(ipClientInfo); aInstantiator.Append(ipClientInfo); aInstantiator.AppendLiteral("|"); nsCOMPtr oopClientExe; if (a11y::GetInstantiator(getter_AddRefs(oopClientExe))) { nsAutoString oopClientInfo; if (NS_SUCCEEDED(oopClientExe->GetPath(oopClientInfo))) { aInstantiator.Append(oopClientInfo); } } #else aInstantiator.Truncate(); #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::EnsureContentProcess() { if (!XRE_IsParentProcess()) return NS_ERROR_NOT_AVAILABLE; RefPtr unused = ContentParent::GetNewOrUsedBrowserProcess(DEFAULT_REMOTE_TYPE); return NS_OK; } NS_IMETHODIMP nsXULAppInfo::InvalidateCachesOnRestart() { nsCOMPtr 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; } 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) { PreferenceSheet::EnsureInitialized(); *aResult = PreferenceSheet::ColorSchemeForChrome() == ColorScheme::Dark; 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::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 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(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 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 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 greBinDir; NS_GetSpecialDirectory(NS_GRE_BIN_DIR, getter_AddRefs(greBinDir)); if (!greBinDir) { return NS_ERROR_FAILURE; } nsCOMPtr xreBinDirectory = greBinDir; if (!xreBinDirectory) { return NS_ERROR_FAILURE; } return CrashReporter::SetExceptionHandler(xreBinDirectory, true); } if (!CrashReporter::GetEnabled()) { // no point in erroring for double-disabling return NS_OK; } return CrashReporter::UnsetExceptionHandler(); } 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 uri; NS_NewURI(getter_AddRefs(uri), data); if (!uri) return NS_ERROR_FAILURE; nsCOMPtr 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 (!aServerURL->SchemeIs("http") && !aServerURL->SchemeIs("https")) { 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; nsresult rv = NS_NewLocalFile(path, false, aMinidumpPath); NS_ENSURE_SUCCESS(rv, rv); return NS_OK; } 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, const nsACString& data) { CrashReporter::Annotation annotation; if (!AnnotationFromString(annotation, PromiseFlatCString(key).get())) { return NS_ERROR_INVALID_ARG; } CrashReporter::RecordAnnotationNSCString(annotation, data); return NS_OK; } NS_IMETHODIMP nsXULAppInfo::RemoveCrashReportAnnotation(const nsACString& key) { CrashReporter::Annotation annotation; if (!AnnotationFromString(annotation, PromiseFlatCString(key).get())) { return NS_ERROR_INVALID_ARG; } CrashReporter::UnrecordAnnotation(annotation); return NS_OK; } NS_IMETHODIMP nsXULAppInfo::IsAnnotationAllowedForPing(const nsACString& aValue, bool* aIsAllowed) { CrashReporter::Annotation annotation; if (!AnnotationFromString(annotation, PromiseFlatCString(aValue).get())) { return NS_ERROR_INVALID_ARG; } *aIsAllowed = CrashReporter::IsAnnotationAllowedForPing(annotation); return NS_OK; } NS_IMETHODIMP nsXULAppInfo::AppendAppNotesToCrashReport(const nsACString& data) { return CrashReporter::AppendAppNotesToCrashReport(data); } NS_IMETHODIMP nsXULAppInfo::RegisterAppMemory(uint64_t pointer, uint64_t len) { return CrashReporter::RegisterAppMemory((void*)pointer, len); } NS_IMETHODIMP nsXULAppInfo::WriteMinidumpForException(void* aExceptionInfo) { #ifdef XP_WIN return CrashReporter::WriteMinidumpForException( static_cast(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 file; nsresult rv = CrashReporter::GetDefaultMemoryReportFile(getter_AddRefs(file)); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } nsString path; file->GetPath(path); nsCOMPtr 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 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(&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 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 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 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 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 creator(components::AppStartup::Service()); if (!creator) return NS_ERROR_UNEXPECTED; nsCOMPtr wwatch( do_GetService(NS_WINDOWWATCHER_CONTRACTID, &rv)); NS_ENSURE_SUCCESS(rv, rv); return wwatch->SetWindowCreator(creator); } /* static */ already_AddRefed NS_GetNativeAppSupport() { if (!ScopedXPCOMStartup::gNativeAppSupport) { return nullptr; } return do_AddRef(ScopedXPCOMStartup::gNativeAppSupport); } nsINativeAppSupport* ScopedXPCOMStartup::gNativeAppSupport; static void DumpArbitraryHelp() { nsresult rv; ScopedLogging log; { ScopedXPCOMStartup xpcom; xpcom.Initialize(); nsCOMPtr 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 Start with .\n" " --profile Start with profile at .\n" " --migration Start with migration wizard.\n" " --ProfileManager Start with ProfileManager.\n" #ifdef MOZ_HAS_REMOTE " --no-remote Do not accept or send remote commands; implies\n" " --new-instance.\n" " --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= Treated as MOZ_LOG= environment " "variable,\n" " overrides it.\n" " --MOZ_LOG_FILE= Treated as MOZ_LOG_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 // 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 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("-os-restarted"); wchar_t** restartArgvConverted = AllocConvertUTF8toUTF16Strings(gRestartArgc, gRestartArgv); gRestartArgv[0] = exeName; mozilla::UniquePtr 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> reflectResult = prefetchRegInfo.ReflectPrefToRegistry(prefVal); MOZ_ASSERT(reflectResult.value.isOk()); } static void SetupAlteredPrefetchPref() { mozilla::DllPrefetchExperimentRegistryInfo prefetchRegInfo; mozilla::DebugOnly> 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 void ReflectSkeletonUIPrefToRegistry(const char* aPref, void* aData) { Unused << aPref; Unused << aData; bool shouldBeEnabled = Preferences::GetBool(kPrefPreXulSkeletonUI, false) && Preferences::GetBool(kPrefBrowserStartupBlankWindow, false) && LookAndFeel::DrawInTitlebar(); if (shouldBeEnabled && Preferences::HasUserValue(kPrefThemeId)) { nsCString themeId; Preferences::GetCString(kPrefThemeId, themeId); if (themeId.EqualsLiteral("default-theme@mozilla.org")) { Unused << SetPreXULSkeletonUIThemeId(ThemeMode::Default); } else if (themeId.EqualsLiteral("firefox-compact-dark@mozilla.org")) { Unused << SetPreXULSkeletonUIThemeId(ThemeMode::Dark); } else if (themeId.EqualsLiteral("firefox-compact-light@mozilla.org")) { Unused << SetPreXULSkeletonUIThemeId(ThemeMode::Light); } else { shouldBeEnabled = false; } } else if (shouldBeEnabled) { Unused << SetPreXULSkeletonUIThemeId(ThemeMode::Default); } if (GetPreXULSkeletonUIEnabled() != shouldBeEnabled) { Unused << SetPreXULSkeletonUIEnabledIfAllowed(shouldBeEnabled); } } 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())); } # 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 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 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 enabledState = launcherRegInfo.IsEnabled(); if (enabledState.isOk()) { gLauncherProcessState = Some(enabledState.unwrap()); CrashReporter::RecordAnnotationU32( CrashReporter::Annotation::LauncherProcessState, static_cast(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 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 binaryPath; rv = XRE_GetBinaryPath(getter_AddRefs(binaryPath)); NS_ENSURE_SUCCESS(rv, rv); nsCOMPtr 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 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 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 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 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. #ifdef MOZ_JPROF // make sure JPROF doesn't think we're E10s unsetenv("JPROF_ISCHILD"); #endif if (aBlankCommandLine) { gRestartArgc = 1; gRestartArgv[gRestartArgc] = nullptr; } #if defined(MOZ_HAS_REMOTE) if (gRestartWithoutRemote) { SaveToEnv("MOZ_NO_REMOTE=1"); } #endif 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); LaunchChildMac(gRestartArgc, gRestartArgv); # else nsCOMPtr 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 // WP_WIN # endif // WP_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. Output(false, "Could not determine any profile running in backgroundtask mode!\n"); return NS_ERROR_ABORT; } # endif // MOZ_BACKGROUNDTASKS nsresult rv; ScopedXPCOMStartup xpcom; rv = xpcom.Initialize(); NS_ENSURE_SUCCESS(rv, rv); rv = xpcom.SetWindowCreator(aNative); NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE); { // extra scoping is needed so we release these components before xpcom // shutdown nsCOMPtr sbs = mozilla::components::StringBundle::Service(); NS_ENSURE_TRUE(sbs, NS_ERROR_FAILURE); nsCOMPtr sb; sbs->CreateBundle(kProfileProperties, getter_AddRefs(sb)); NS_ENSURE_TRUE_LOG(sbs, NS_ERROR_FAILURE); NS_ConvertUTF8toUTF16 appName(gAppData->name); AutoTArray 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 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 } static ReturnAbortOnError ProfileLockedDialog(nsIFile* aProfileDir, nsIFile* aProfileLocalDir, nsIProfileUnlocker* aUnlocker, nsINativeAppSupport* aNative, nsIProfileLock** aResult) { nsresult rv; 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); { // extra scoping is needed so we release these components before xpcom // shutdown nsCOMPtr sbs = mozilla::components::StringBundle::Service(); NS_ENSURE_TRUE(sbs, NS_ERROR_FAILURE); nsCOMPtr sb; sbs->CreateBundle(kProfileProperties, getter_AddRefs(sb)); NS_ENSURE_TRUE_LOG(sbs, NS_ERROR_FAILURE); NS_ConvertUTF8toUTF16 appName(gAppData->name); AutoTArray 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 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); #if defined(MOZ_HAS_REMOTE) if (gRemoteService) { gRemoteService->UnlockStartup(); gRemoteService = nullptr; } #endif return LaunchChild(false, true); } } else { rv = ps->Alert(nullptr, killTitle.get(), killMessage.get()); NS_ENSURE_SUCCESS_LOG(rv, rv); } return NS_ERROR_ABORT; } } static const char kProfileManagerURL[] = "chrome://mozapps/content/profile/profileSelection.xhtml"; static ReturnAbortOnError ShowProfileManager( nsIToolkitProfileService* aProfileSvc, nsINativeAppSupport* aNative) { nsresult rv; nsCOMPtr profD, profLD; bool offline = false; int32_t dialogReturn; { 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 windowWatcher( do_GetService(NS_WINDOWWATCHER_CONTRACTID)); nsCOMPtr ioParamBlock( do_CreateInstance(NS_DIALOGPARAMBLOCK_CONTRACTID)); nsCOMPtr dlgArray( do_CreateInstance(NS_ARRAY_CONTRACTID)); NS_ENSURE_TRUE(windowWatcher && ioParamBlock && dlgArray, NS_ERROR_FAILURE); ioParamBlock->SetObjects(dlgArray); nsCOMPtr 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 newWindow; rv = windowWatcher->OpenWindow( nullptr, nsDependentCString(kProfileManagerURL), "_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 (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"); SaveToEnv("XRE_RESTARTED_BY_PROFILE_MANAGER=1"); } else { MOZ_ASSERT(dialogReturn == nsIToolkitProfileService::launchWithProfile); SaveFileToEnv("XRE_PROFILE_PATH", profD); SaveFileToEnv("XRE_PROFILE_LOCAL_PATH", profLD); SaveToEnv("XRE_RESTARTED_BY_PROFILE_MANAGER=1"); } 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("-os-restarted"); gRestartArgv[gRestartArgc] = nullptr; } #if defined(MOZ_HAS_REMOTE) if (gRemoteService) { gRemoteService->UnlockStartup(); gRemoteService = nullptr; } #endif return LaunchChild(false, true); } static bool gDoMigration = false; static bool gDoProfileReset = false; static nsCOMPtr 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 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 // 2) check for -P // 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 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; } // 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& aStr) final { fprintf(mFile, "%.*s", int(aStr.size()), aStr.data()); } }; static void SubmitDowngradeTelemetry(const nsCString& aLastVersion, bool aHasSync, int32_t aButton) { nsCOMPtr prefSvc = do_GetService("@mozilla.org/preferences-service;1"); NS_ENSURE_TRUE_VOID(prefSvc); nsCOMPtr 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; } nsCString server; rv = prefBranch->GetCharPref("toolkit.telemetry.server", server); NS_ENSURE_SUCCESS_VOID(rv); nsCString clientId; rv = prefBranch->GetCharPref("toolkit.telemetry.cachedClientID", clientId); NS_ENSURE_SUCCESS_VOID(rv); 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); nsCString arch("null"); nsCOMPtr sysInfo = do_GetService("@mozilla.org/system-info;1"); NS_ENSURE_TRUE_VOID(sysInfo); sysInfo->GetPropertyAsACString(u"arch"_ns, arch); time_t now; time(&now); char date[sizeof "YYYY-MM-DDThh:mm:ss.000Z"]; strftime(date, sizeof date, "%FT%T.000Z", gmtime(&now)); NSID_TrimBracketsASCII pingId(uuid); constexpr auto pingType = "downgrade"_ns; int32_t pos = aLastVersion.Find("_"); if (pos == kNotFound) { return; } const nsDependentCSubstring lastVersion = Substring(aLastVersion, 0, pos); const nsDependentCSubstring lastBuildId = Substring(aLastVersion, pos + 1, 14); nsPrintfCString url("%s/submit/telemetry/%s/%s/%s/%s/%s/%s?v=%d", server.get(), PromiseFlatCString(pingId).get(), pingType.get(), (const char*)gAppData->name, (const char*)gAppData->version, channel.get(), (const char*)gAppData->buildID, TELEMETRY_PING_FORMAT_VERSION); nsCOMPtr pingFile; rv = NS_GetSpecialDirectory(XRE_USER_APP_DATA_DIR, getter_AddRefs(pingFile)); NS_ENSURE_SUCCESS_VOID(rv); rv = pingFile->Append(u"Pending Pings"_ns); NS_ENSURE_SUCCESS_VOID(rv); rv = pingFile->Create(nsIFile::DIRECTORY_TYPE, 0755); if (NS_FAILED(rv) && rv != NS_ERROR_FILE_ALREADY_EXISTS) { return; } rv = pingFile->Append(NS_ConvertUTF8toUTF16(pingId)); NS_ENSURE_SUCCESS_VOID(rv); nsCOMPtr 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; } FILE* file; rv = pingFile->OpenANSIFileDesc("w", &file); NS_ENSURE_SUCCESS_VOID(rv); JSONWriter w(MakeUnique(file)); w.Start(); { w.StringProperty("type", Span(pingType.Data(), pingType.Length())); w.StringProperty("id", PromiseFlatCString(pingId)); w.StringProperty("creationDate", MakeStringSpan(date)); w.IntProperty("version", TELEMETRY_PING_FORMAT_VERSION); w.StringProperty("clientId", clientId); w.StartObjectProperty("application"); { w.StringProperty("architecture", arch); w.StringProperty( "buildId", MakeStringSpan(static_cast(gAppData->buildID))); w.StringProperty( "name", MakeStringSpan(static_cast(gAppData->name))); w.StringProperty( "version", MakeStringSpan(static_cast(gAppData->version))); w.StringProperty("displayVersion", MOZ_STRINGIFY(MOZ_APP_VERSION_DISPLAY)); w.StringProperty( "vendor", MakeStringSpan(static_cast(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", channel); } w.EndObject(); w.StartObjectProperty("payload"); { w.StringProperty("lastVersion", PromiseFlatCString(lastVersion)); w.StringProperty("lastBuildId", PromiseFlatCString(lastBuildId)); w.BoolProperty("hasSync", aHasSync); w.IntProperty("button", aButton); } w.EndObject(); } w.End(); fclose(file); PathString filePath = pingFile->NativePath(); 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 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"; static ReturnAbortOnError CheckDowngrade(nsIFile* aProfileDir, nsINativeAppSupport* aNative, nsIToolkitProfileService* aProfileSvc, const nsCString& aLastVersion) { 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); { // extra scoping is needed so we release these components before xpcom // shutdown bool hasSync = false; nsCOMPtr prefSvc = do_GetService("@mozilla.org/preferences-service;1"); NS_ENSURE_TRUE(prefSvc, rv); nsCOMPtr 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 prefBranch = do_QueryInterface(prefSvc); rv = prefBranch->PrefHasUserValue("services.sync.username", &hasSync); NS_ENSURE_SUCCESS(rv, rv); } nsCOMPtr windowWatcher = do_GetService(NS_WINDOWWATCHER_CONTRACTID); NS_ENSURE_TRUE(windowWatcher, NS_ERROR_ABORT); nsCOMPtr appStartup(components::AppStartup::Service()); NS_ENSURE_TRUE(appStartup, NS_ERROR_FAILURE); nsCOMPtr 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 newWindow; rv = windowWatcher->OpenWindow( nullptr, nsDependentCString(kProfileDowngradeURL), "_blank"_ns, features, paramBlock, getter_AddRefs(newWindow)); NS_ENSURE_SUCCESS(rv, rv); paramBlock->GetInt(1, &result); SubmitDowngradeTelemetry(aLastVersion, hasSync, result); } } 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 newProfile; rv = aProfileSvc->CreateUniqueProfile(nullptr, profileName, getter_AddRefs(newProfile)); NS_ENSURE_SUCCESS(rv, rv); rv = aProfileSvc->SetDefaultProfile(newProfile); NS_ENSURE_SUCCESS(rv, rv); rv = aProfileSvc->Flush(); NS_ENSURE_SUCCESS(rv, rv); nsCOMPtr profD, profLD; rv = newProfile->GetRootDir(getter_AddRefs(profD)); NS_ENSURE_SUCCESS(rv, rv); rv = newProfile->GetLocalDir(getter_AddRefs(profLD)); NS_ENSURE_SUCCESS(rv, rv); SaveFileToEnv("XRE_PROFILE_PATH", profD); SaveFileToEnv("XRE_PROFILE_LOCAL_PATH", profLD); return LaunchChild(false, true); } // Cancel return NS_ERROR_ABORT; } #endif /** * Extracts the various parts of a compatibility version string. * * Compatibility versions are of the form * "_/". The toolkit version comparator * can only handle 32-bit numbers and in the normal case build IDs are larger * than this. So if the build ID is numeric we split it into two version parts. */ static void ExtractCompatVersionInfo(const nsACString& aCompatVersion, nsACString& aAppVersion, nsACString& aAppBuildID) { int32_t underscorePos = aCompatVersion.FindChar('_'); int32_t slashPos = aCompatVersion.FindChar('/'); if (underscorePos == kNotFound || slashPos == kNotFound || slashPos < underscorePos) { NS_WARNING( "compatibility.ini Version string does not match the expected format."); // Fall back to just using the entire string as the version. aAppVersion = aCompatVersion; aAppBuildID.Truncate(0); return; } aAppVersion = Substring(aCompatVersion, 0, underscorePos); aAppBuildID = Substring(aCompatVersion, underscorePos + 1, slashPos - (underscorePos + 1)); } /** * Compares the provided compatibility versions. Returns 0 if they match, * < 0 if the new version is considered an upgrade from the old version and * > 0 if the new version is considered a downgrade from the old version. */ int32_t CompareCompatVersions(const nsACString& aOldCompatVersion, const nsACString& aNewCompatVersion) { // Hardcode the case where the last run was in safe mode (Bug 1556612). We // cannot tell if this is a downgrade or not so just assume it isn't and let // the user proceed. if (aOldCompatVersion.EqualsLiteral("Safe Mode")) { return -1; } // Extract the major version part from the version string and only use that // for version comparison. int32_t index = aOldCompatVersion.FindChar('.'); const nsACString& oldMajorVersion = Substring( aOldCompatVersion, 0, index < 0 ? aOldCompatVersion.Length() : index); index = aNewCompatVersion.FindChar('.'); const nsACString& newMajorVersion = Substring( aNewCompatVersion, 0, index < 0 ? aNewCompatVersion.Length() : index); return CompareVersions(PromiseFlatCString(oldMajorVersion).get(), PromiseFlatCString(newMajorVersion).get()); } /** * Checks the compatibility.ini file to see if we have updated our application * or otherwise invalidated our caches. If the application has been updated, * we return false; otherwise, we return true. * * We also write the status of the caches (valid/invalid) into the return param * aCachesOK. The aCachesOK is always invalid if the application has been * updated. * * Finally, aIsDowngrade is set to true if the current application is older * than that previously used by the profile. */ static bool CheckCompatibility(nsIFile* aProfileDir, const nsCString& aVersion, const nsCString& aOSABI, nsIFile* aXULRunnerDir, nsIFile* aAppDir, nsIFile* aFlagFile, bool* aCachesOK, bool* aIsDowngrade, nsCString& aLastVersion) { *aCachesOK = false; *aIsDowngrade = false; gLastAppVersion.SetIsVoid(true); gLastAppBuildID.SetIsVoid(true); nsCOMPtr file; aProfileDir->Clone(getter_AddRefs(file)); if (!file) return false; file->AppendNative(FILE_COMPATIBILITY_INFO); nsINIParser parser; nsresult rv = parser.Init(file); if (NS_FAILED(rv)) return false; rv = parser.GetString("Compatibility", "LastVersion", aLastVersion); if (NS_FAILED(rv)) { return false; } if (!aLastVersion.Equals(aVersion)) { // The version is not the same. Whether it's a downgrade depends on an // actual comparison: *aIsDowngrade = 0 < CompareCompatVersions(aLastVersion, aVersion); ExtractCompatVersionInfo(aLastVersion, gLastAppVersion, gLastAppBuildID); return false; } // If we get here, the version matched, but there may still be other // differences between us and the build that the profile last ran under. gLastAppVersion.Assign(gAppData->version); gLastAppBuildID.Assign(gAppData->buildID); nsAutoCString buf; rv = parser.GetString("Compatibility", "LastOSABI", buf); if (NS_FAILED(rv) || !aOSABI.Equals(buf)) return false; rv = parser.GetString("Compatibility", "LastPlatformDir", buf); if (NS_FAILED(rv)) return false; nsCOMPtr lf; rv = NS_NewNativeLocalFile(""_ns, false, getter_AddRefs(lf)); if (NS_FAILED(rv)) return false; rv = lf->SetPersistentDescriptor(buf); if (NS_FAILED(rv)) return false; bool eq; rv = lf->Equals(aXULRunnerDir, &eq); if (NS_FAILED(rv) || !eq) return false; if (aAppDir) { rv = parser.GetString("Compatibility", "LastAppDir", buf); if (NS_FAILED(rv)) return false; rv = NS_NewNativeLocalFile(""_ns, false, getter_AddRefs(lf)); if (NS_FAILED(rv)) return false; rv = lf->SetPersistentDescriptor(buf); if (NS_FAILED(rv)) return false; rv = lf->Equals(aAppDir, &eq); if (NS_FAILED(rv) || !eq) return false; } // If we see this flag, caches are invalid. rv = parser.GetString("Compatibility", "InvalidateCaches", buf); *aCachesOK = (NS_FAILED(rv) || !buf.EqualsLiteral("1")); bool purgeCaches = false; if (aFlagFile && NS_SUCCEEDED(aFlagFile->Exists(&purgeCaches)) && purgeCaches) { *aCachesOK = false; } return true; } void BuildCompatVersion(const char* aAppVersion, const char* aAppBuildID, const char* aToolkitBuildID, nsACString& aBuf) { aBuf.Assign(aAppVersion); aBuf.Append('_'); aBuf.Append(aAppBuildID); aBuf.Append('/'); aBuf.Append(aToolkitBuildID); } static void BuildVersion(nsCString& aBuf) { BuildCompatVersion(gAppData->version, gAppData->buildID, gToolkitBuildID, aBuf); } static void WriteVersion(nsIFile* aProfileDir, const nsCString& aVersion, const nsCString& aOSABI, nsIFile* aXULRunnerDir, nsIFile* aAppDir, bool invalidateCache) { nsCOMPtr file; aProfileDir->Clone(getter_AddRefs(file)); if (!file) return; file->AppendNative(FILE_COMPATIBILITY_INFO); nsAutoCString platformDir; Unused << aXULRunnerDir->GetPersistentDescriptor(platformDir); nsAutoCString appDir; if (aAppDir) Unused << aAppDir->GetPersistentDescriptor(appDir); PRFileDesc* fd; nsresult rv = file->OpenNSPRFileDesc(PR_WRONLY | PR_CREATE_FILE | PR_TRUNCATE, 0600, &fd); if (NS_FAILED(rv)) { NS_ERROR("could not create output stream"); return; } static const char kHeader[] = "[Compatibility]" NS_LINEBREAK "LastVersion="; PR_Write(fd, kHeader, sizeof(kHeader) - 1); PR_Write(fd, aVersion.get(), aVersion.Length()); static const char kOSABIHeader[] = NS_LINEBREAK "LastOSABI="; PR_Write(fd, kOSABIHeader, sizeof(kOSABIHeader) - 1); PR_Write(fd, aOSABI.get(), aOSABI.Length()); static const char kPlatformDirHeader[] = NS_LINEBREAK "LastPlatformDir="; PR_Write(fd, kPlatformDirHeader, sizeof(kPlatformDirHeader) - 1); PR_Write(fd, platformDir.get(), platformDir.Length()); static const char kAppDirHeader[] = NS_LINEBREAK "LastAppDir="; if (aAppDir) { PR_Write(fd, kAppDirHeader, sizeof(kAppDirHeader) - 1); PR_Write(fd, appDir.get(), appDir.Length()); } static const char kInvalidationHeader[] = NS_LINEBREAK "InvalidateCaches=1"; if (invalidateCache) PR_Write(fd, kInvalidationHeader, sizeof(kInvalidationHeader) - 1); static const char kNL[] = NS_LINEBREAK; PR_Write(fd, kNL, sizeof(kNL) - 1); PR_Close(fd); } /** * Returns true if the startup cache file was successfully removed. * Returns false if file->Clone fails at any point (OOM) or if unable * to remove the startup cache file. Note in particular the return value * is unaffected by a failure to remove extensions.ini */ static bool RemoveComponentRegistries(nsIFile* aProfileDir, nsIFile* aLocalProfileDir, bool aRemoveEMFiles) { nsCOMPtr file; aProfileDir->Clone(getter_AddRefs(file)); if (!file) return false; if (aRemoveEMFiles) { file->SetNativeLeafName("extensions.ini"_ns); file->Remove(false); } aLocalProfileDir->Clone(getter_AddRefs(file)); if (!file) return false; file->AppendNative("startupCache"_ns); nsresult rv = file->Remove(true); return NS_SUCCEEDED(rv) || rv == NS_ERROR_FILE_NOT_FOUND; } // When we first initialize the crash reporter we don't have a profile, // so we set the minidump path to $TEMP. Once we have a profile, // we set it to $PROFILE/minidumps, creating the directory // if needed. static void MakeOrSetMinidumpPath(nsIFile* profD) { nsCOMPtr dumpD; profD->Clone(getter_AddRefs(dumpD)); if (dumpD) { bool fileExists; // XXX: do some more error checking here dumpD->Append(u"minidumps"_ns); dumpD->Exists(&fileExists); if (!fileExists) { nsresult rv = dumpD->Create(nsIFile::DIRECTORY_TYPE, 0700); NS_ENSURE_SUCCESS_VOID(rv); } nsAutoString pathStr; if (NS_SUCCEEDED(dumpD->GetPath(pathStr))) CrashReporter::SetMinidumpPath(pathStr); } } const XREAppData* gAppData = nullptr; /** * NSPR will search for the "nspr_use_zone_allocator" symbol throughout * the process and use it to determine whether the application defines its own * memory allocator or not. * * Since most applications (e.g. Firefox and Thunderbird) don't use any special * allocators and therefore don't define this symbol, NSPR must search the * entire process, which reduces startup performance. * * By defining the symbol here, we can avoid the wasted lookup and hopefully * improve startup performance. */ NS_VISIBILITY_DEFAULT PRBool nspr_use_zone_allocator = PR_FALSE; #ifdef CAIRO_HAS_DWRITE_FONT # include # include "nsWindowsHelpers.h" # ifdef DEBUG_DWRITE_STARTUP # define LOGREGISTRY(msg) LogRegistryEvent(msg) // for use when monitoring process static void LogRegistryEvent(const wchar_t* msg) { HKEY dummyKey; HRESULT hr; wchar_t buf[512]; wsprintf(buf, L" log %s", msg); hr = RegOpenKeyEx(HKEY_LOCAL_MACHINE, buf, 0, KEY_READ, &dummyKey); if (SUCCEEDED(hr)) { RegCloseKey(dummyKey); } } # else # define LOGREGISTRY(msg) # endif static DWORD WINAPI InitDwriteBG(LPVOID lpdwThreadParam) { SetThreadPriority(GetCurrentThread(), THREAD_MODE_BACKGROUND_BEGIN); LOGREGISTRY(L"loading dwrite.dll"); HMODULE dwdll = LoadLibrarySystem32(L"dwrite.dll"); if (dwdll) { decltype(DWriteCreateFactory)* createDWriteFactory = (decltype(DWriteCreateFactory)*)GetProcAddress(dwdll, "DWriteCreateFactory"); if (createDWriteFactory) { LOGREGISTRY(L"creating dwrite factory"); IDWriteFactory* factory; HRESULT hr = createDWriteFactory(DWRITE_FACTORY_TYPE_SHARED, __uuidof(IDWriteFactory), reinterpret_cast(&factory)); if (SUCCEEDED(hr)) { LOGREGISTRY(L"dwrite factory done"); factory->Release(); LOGREGISTRY(L"freed factory"); } else { LOGREGISTRY(L"failed to create factory"); } } } SetThreadPriority(GetCurrentThread(), THREAD_MODE_BACKGROUND_END); return 0; } #endif #include "GeckoProfiler.h" #include "ProfilerControl.h" // Encapsulates startup and shutdown state for XRE_main class XREMain { public: XREMain() = default; ~XREMain() { mScopedXPCOM = nullptr; mAppData = nullptr; } int XRE_main(int argc, char* argv[], const BootstrapConfig& aConfig); int XRE_mainInit(bool* aExitFlag); int XRE_mainStartup(bool* aExitFlag); nsresult XRE_mainRun(); bool CheckLastStartupWasCrash(); nsCOMPtr mNativeApp; RefPtr mProfileSvc; nsCOMPtr mProfD; nsCOMPtr mProfLD; nsCOMPtr mProfileLock; #if defined(MOZ_HAS_REMOTE) RefPtr mRemoteService; #endif UniquePtr mScopedXPCOM; UniquePtr mAppData; nsXREDirProvider mDirProvider; #ifdef MOZ_WIDGET_GTK nsAutoCString mXDGActivationToken; nsAutoCString mDesktopStartupID; #endif bool mStartOffline = false; #if defined(MOZ_HAS_REMOTE) bool mDisableRemoteClient = false; bool mDisableRemoteServer = false; #endif }; #if defined(XP_UNIX) && !defined(ANDROID) static SmprintfPointer FormatUid(uid_t aId) { if (const auto pw = getpwuid(aId)) { return mozilla::Smprintf("%s", pw->pw_name); } return mozilla::Smprintf("uid %d", static_cast(aId)); } // Bug 1323302: refuse to run under sudo or similar. static bool CheckForUserMismatch() { static char const* const kVars[] = { "HOME", # ifdef MOZ_WIDGET_GTK "XDG_RUNTIME_DIR", # endif # ifdef MOZ_X11 "XAUTHORITY", # endif }; const uid_t euid = geteuid(); if (euid != 0) { // On Linux it's possible to have superuser capabilities with a // nonzero uid, but anyone who knows enough to make that happen // probably knows enough to debug the resulting problems. // Otherwise, a non-root user can't cause the problems we're // concerned about. return false; } for (const auto var : kVars) { if (const auto path = PR_GetEnv(var)) { struct stat st; if (stat(path, &st) == 0) { if (st.st_uid != euid) { const auto owner = FormatUid(st.st_uid); Output(true, "Running " MOZ_APP_DISPLAYNAME " as root in a regular" " user's session is not supported. ($%s is %s which is" " owned by %s.)\n", var, path, owner.get()); return true; } } } } return false; } #else // !XP_UNIX || ANDROID static bool CheckForUserMismatch() { return false; } #endif void mozilla::startup::IncreaseDescriptorLimits() { #ifdef XP_UNIX // Increase the fd limit to accomodate IPC resources like shared memory. // See also the Darwin case in config/external/nspr/pr/moz.build static const rlim_t kFDs = 4096; struct rlimit rlim; if (getrlimit(RLIMIT_NOFILE, &rlim) != 0) { Output(false, "getrlimit: %s\n", strerror(errno)); return; } // Don't decrease the limit if it's already high enough, but don't // try to go over the hard limit. (RLIM_INFINITY isn't required to // be the numerically largest rlim_t, so don't assume that.) if (rlim.rlim_cur != RLIM_INFINITY && rlim.rlim_cur < kFDs && rlim.rlim_cur < rlim.rlim_max) { if (rlim.rlim_max != RLIM_INFINITY && rlim.rlim_max < kFDs) { rlim.rlim_cur = rlim.rlim_max; } else { rlim.rlim_cur = kFDs; } if (setrlimit(RLIMIT_NOFILE, &rlim) != 0) { Output(false, "setrlimit: %s\n", strerror(errno)); } } #endif } #ifdef XP_WIN static uint32_t GetMicrocodeVersionByVendor(HKEY key, DWORD upper, DWORD lower) { WCHAR data[13]; // The CPUID vendor string is 12 characters long plus null DWORD len = sizeof(data); DWORD vtype; if (RegQueryValueExW(key, L"VendorIdentifier", nullptr, &vtype, reinterpret_cast(data), &len) == ERROR_SUCCESS) { if (wcscmp(L"GenuineIntel", data) == 0) { // Intel reports the microcode version in the upper 32 bits of the MSR return upper; } if (wcscmp(L"AuthenticAMD", data) == 0) { // AMD reports the microcode version in the lower 32 bits of the MSR return lower; } // Unknown CPU vendor, return whatever half is non-zero return lower ? lower : upper; } return 0; // No clue } #endif // XP_WIN static void MaybeAddCPUMicrocodeCrashAnnotation() { #ifdef XP_WIN // Add CPU microcode version to the crash report as "CPUMicrocodeVersion". // It feels like this code may belong in nsSystemInfo instead. uint32_t cpuUpdateRevision = 0; HKEY key; static const WCHAR keyName[] = L"HARDWARE\\DESCRIPTION\\System\\CentralProcessor\\0"; if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, keyName, 0, KEY_QUERY_VALUE, &key) == ERROR_SUCCESS) { DWORD updateRevision[2]; DWORD len = sizeof(updateRevision); DWORD vtype; // Windows 7 uses "Update Signature", 8 uses "Update Revision". // For AMD CPUs, "CurrentPatchLevel" is sometimes used. // Take the first one we find. LPCWSTR choices[] = {L"Update Signature", L"Update Revision", L"CurrentPatchLevel"}; for (const auto& oneChoice : choices) { if (RegQueryValueExW(key, oneChoice, nullptr, &vtype, reinterpret_cast(updateRevision), &len) == ERROR_SUCCESS) { if (vtype == REG_BINARY && len == sizeof(updateRevision)) { cpuUpdateRevision = GetMicrocodeVersionByVendor( key, updateRevision[1], updateRevision[0]); break; } if (vtype == REG_DWORD && len == sizeof(updateRevision[0])) { cpuUpdateRevision = static_cast(updateRevision[0]); break; } } } } if (cpuUpdateRevision > 0) { CrashReporter::RecordAnnotationNSCString( CrashReporter::Annotation::CPUMicrocodeVersion, nsPrintfCString("0x%" PRIx32, cpuUpdateRevision)); } #endif } #if defined(MOZ_BACKGROUNDTASKS) static void SetupConsoleForBackgroundTask( const nsCString& aBackgroundTaskName) { // We do not suppress output on Windows because: // 1. Background task subprocesses launched via LaunchApp() does not attach to // the console. // 2. Suppressing output intermittently causes failures on when running // multiple tasks (see bug 1831631) # ifndef XP_WIN if (BackgroundTasks::IsNoOutputTaskName(aBackgroundTaskName) && !CheckArg("attach-console") && !EnvHasValue("MOZ_BACKGROUNDTASKS_IGNORE_NO_OUTPUT")) { // Suppress output, somewhat crudely. We need to suppress stderr as well // as stdout because assertions, of which there are many, write to stderr. Unused << freopen("/dev/null", "w", stdout); Unused << freopen("/dev/null", "w", stderr); return; } # endif printf_stderr("*** You are running in background task mode. ***\n"); } #endif /* * XRE_mainInit - Initial setup and command line parameter processing. * Main() will exit early if either return value != 0 or if aExitFlag is * true. */ int XREMain::XRE_mainInit(bool* aExitFlag) { if (!aExitFlag) return 1; *aExitFlag = false; atexit(UnexpectedExit); auto expectedShutdown = mozilla::MakeScopeExit([&] { MozExpectedExit(); }); StartupTimeline::Record(StartupTimeline::MAIN); if (CheckForUserMismatch()) { return 1; } #ifdef XP_MACOSX mozilla::MacAutoreleasePool pool; DisableAppNap(); #endif #ifdef MOZ_BACKGROUNDTASKS Maybe backgroundTask = Nothing(); const char* backgroundTaskName = nullptr; if (ARG_FOUND == CheckArg("backgroundtask", &backgroundTaskName, CheckArgFlag::None)) { backgroundTask = Some(backgroundTaskName); SetupConsoleForBackgroundTask(backgroundTask.ref()); } BackgroundTasks::Init(backgroundTask); #endif #ifndef ANDROID if (PR_GetEnv("MOZ_RUN_GTEST") # ifdef FUZZING || PR_GetEnv("FUZZER") # endif # ifdef MOZ_BACKGROUNDTASKS || BackgroundTasks::IsBackgroundTaskMode() # endif ) { // Enable headless mode and assert that it worked, since gfxPlatform // uses a static bool set after the first call to `IsHeadless`. // Note: Android gtests seem to require an Activity and fail to start // with headless mode enabled. PR_SetEnv("MOZ_HEADLESS=1"); MOZ_ASSERT(gfxPlatform::IsHeadless()); } #endif // ANDROID if (PR_GetEnv("MOZ_CHAOSMODE")) { ChaosFeature feature = ChaosFeature::Any; long featureInt = strtol(PR_GetEnv("MOZ_CHAOSMODE"), nullptr, 16); if (featureInt) { // NOTE: MOZ_CHAOSMODE=0 or a non-hex value maps to Any feature. feature = static_cast(featureInt); } ChaosMode::SetChaosFeature(feature); } if (CheckArgExists("fxr")) { gFxREmbedded = true; } if (ChaosMode::isActive(ChaosFeature::Any)) { printf_stderr( "*** You are running in chaos test mode. See ChaosMode.h. ***\n"); } if (CheckArg("headless") || CheckArgExists("screenshot")) { PR_SetEnv("MOZ_HEADLESS=1"); } if (gfxPlatform::IsHeadless()) { #if defined(XP_WIN) || defined(MOZ_WIDGET_GTK) || defined(XP_MACOSX) printf_stderr("*** You are running in headless mode.\n"); #else Output( true, "Error: headless mode is not currently supported on this platform.\n"); return 1; #endif #ifdef XP_MACOSX // To avoid taking focus when running in headless mode immediately // transition Firefox to a background application. ProcessSerialNumber psn = {0, kCurrentProcess}; OSStatus transformStatus = TransformProcessType(&psn, kProcessTransformToBackgroundApplication); if (transformStatus != noErr) { NS_ERROR("Failed to make process a background application."); return 1; } #endif } gKioskMode = CheckArg("kiosk", nullptr, CheckArgFlag::None); const char* kioskMonitorNumber = nullptr; if (CheckArg("kiosk-monitor", &kioskMonitorNumber, CheckArgFlag::None)) { gKioskMode = true; gKioskMonitor = atoi(kioskMonitorNumber); } gAllowContentAnalysisArgPresent = CheckArg("allow-content-analysis", nullptr, CheckArgFlag::RemoveArg) == ARG_FOUND; nsresult rv; ArgResult ar; #ifdef DEBUG if (PR_GetEnv("XRE_MAIN_BREAK")) NS_BREAK(); #endif mozilla::startup::IncreaseDescriptorLimits(); SetupErrorHandling(gArgv[0]); #ifdef CAIRO_HAS_DWRITE_FONT { // Bug 602792 - when DWriteCreateFactory is called the dwrite client dll // starts the FntCache service if it isn't already running (it's set // to manual startup by default in Windows 7 RTM). Subsequent DirectWrite // calls cause the IDWriteFactory object to communicate with the FntCache // service with a timeout; if there's no response after the timeout, the // DirectWrite client library will assume the service isn't around and do // manual font file I/O on _all_ system fonts. To avoid this, load the // dwrite library and create a factory as early as possible so that the // FntCache service is ready by the time it's needed. CreateThread(nullptr, 0, &InitDwriteBG, nullptr, 0, nullptr); } #endif #ifdef XP_UNIX const char* home = PR_GetEnv("HOME"); if (!home || !*home) { struct passwd* pw = getpwuid(geteuid()); if (!pw || !pw->pw_dir) { Output(true, "Could not determine HOME directory"); return 1; } SaveWordToEnv("HOME", nsDependentCString(pw->pw_dir)); } #endif #ifdef MOZ_ACCESSIBILITY_ATK // Suppress atk-bridge init at startup, until mozilla accessibility is // initialized. This works after gnome 2.24.2. SaveToEnv("NO_AT_BRIDGE=1"); #endif // Check for application.ini overrides const char* override = nullptr; ar = CheckArg("override", &override); if (ar == ARG_BAD) { Output(true, "Incorrect number of arguments passed to --override"); return 1; } if (ar == ARG_FOUND) { nsCOMPtr overrideLF; rv = XRE_GetFileFromPath(override, getter_AddRefs(overrideLF)); if (NS_FAILED(rv)) { Output(true, "Error: unrecognized override.ini path.\n"); return 1; } rv = XRE_ParseAppData(overrideLF, *mAppData); if (NS_FAILED(rv)) { Output(true, "Couldn't read override.ini"); return 1; } } // Check sanity and correctness of app data. if (!mAppData->name) { Output(true, "Error: App:Name not specified in application.ini\n"); return 1; } if (!mAppData->buildID) { Output(true, "Error: App:BuildID not specified in application.ini\n"); return 1; } // XXX Originally ScopedLogging was here? Now it's in XRE_main above // XRE_mainInit. if (!mAppData->minVersion) { Output(true, "Error: Gecko:MinVersion not specified in application.ini\n"); return 1; } if (!mAppData->maxVersion) { // If no maxVersion is specified, we assume the app is only compatible // with the initial preview release. Do not increment this number ever! mAppData->maxVersion = "1.*"; } if (mozilla::Version(mAppData->minVersion) > gToolkitVersion || mozilla::Version(mAppData->maxVersion) < gToolkitVersion) { Output(true, "Error: Platform version '%s' is not compatible with\n" "minVersion >= %s\nmaxVersion <= %s\n", (const char*)gToolkitVersion, (const char*)mAppData->minVersion, (const char*)mAppData->maxVersion); return 1; } rv = mDirProvider.Initialize(mAppData->directory, mAppData->xreDirectory); if (NS_FAILED(rv)) return 1; if (EnvHasValue("MOZ_CRASHREPORTER")) { mAppData->flags |= NS_XRE_ENABLE_CRASH_REPORTER; } nsCOMPtr xreBinDirectory; xreBinDirectory = mDirProvider.GetGREBinDir(); if ((mAppData->flags & NS_XRE_ENABLE_CRASH_REPORTER) && NS_SUCCEEDED(CrashReporter::SetExceptionHandler(xreBinDirectory))) { nsCOMPtr file; rv = nsXREDirProvider::GetUserAppDataDirectory(getter_AddRefs(file)); if (NS_SUCCEEDED(rv)) { CrashReporter::SetUserAppDataDirectory(file); } if (mAppData->crashReporterURL) { CrashReporter::SetServerURL( nsDependentCString(mAppData->crashReporterURL)); } // We overwrite this once we finish starting up. CrashReporter::RecordAnnotationBool(CrashReporter::Annotation::StartupCrash, true); // pass some basic info from the app data if (mAppData->vendor) { CrashReporter::RecordAnnotationCString(CrashReporter::Annotation::Vendor, mAppData->vendor); } if (mAppData->name) { CrashReporter::RecordAnnotationCString( CrashReporter::Annotation::ProductName, mAppData->name); } if (mAppData->ID) { CrashReporter::RecordAnnotationCString( CrashReporter::Annotation::ProductID, mAppData->ID); } if (mAppData->version) { CrashReporter::RecordAnnotationCString(CrashReporter::Annotation::Version, mAppData->version); } if (mAppData->buildID) { CrashReporter::RecordAnnotationCString(CrashReporter::Annotation::BuildID, mAppData->buildID); } nsDependentCString releaseChannel(MOZ_STRINGIFY(MOZ_UPDATE_CHANNEL)); CrashReporter::RecordAnnotationNSCString( CrashReporter::Annotation::ReleaseChannel, releaseChannel); #ifdef XP_WIN nsAutoString appInitDLLs; if (widget::WinUtils::GetAppInitDLLs(appInitDLLs)) { CrashReporter::RecordAnnotationNSString( CrashReporter::Annotation::AppInitDLLs, appInitDLLs); } nsString packageFamilyName = widget::WinUtils::GetPackageFamilyName(); if (StringBeginsWith(packageFamilyName, u"Mozilla."_ns) || StringBeginsWith(packageFamilyName, u"MozillaCorporation."_ns)) { CrashReporter::RecordAnnotationNSCString( CrashReporter::Annotation::WindowsPackageFamilyName, NS_ConvertUTF16toUTF8(packageFamilyName)); } #endif bool isBackgroundTaskMode = false; #ifdef MOZ_BACKGROUNDTASKS Maybe backgroundTasks = BackgroundTasks::GetBackgroundTasks(); if (backgroundTasks.isSome()) { isBackgroundTaskMode = true; CrashReporter::RecordAnnotationNSCString( CrashReporter::Annotation::BackgroundTaskName, backgroundTasks.ref()); } #endif CrashReporter::RecordAnnotationBool( CrashReporter::Annotation::BackgroundTaskMode, isBackgroundTaskMode); CrashReporter::RecordAnnotationBool(CrashReporter::Annotation::HeadlessMode, gfxPlatform::IsHeadless()); CrashReporter::SetRestartArgs(gArgc, gArgv); // annotate other data (user id etc) nsCOMPtr userAppDataDir; if (NS_SUCCEEDED(mDirProvider.GetUserAppDataDirectory( getter_AddRefs(userAppDataDir)))) { CrashReporter::SetupExtraData(userAppDataDir, nsDependentCString(mAppData->buildID)); // see if we have a crashreporter-override.ini in the application // directory nsCOMPtr overrideini; if (NS_SUCCEEDED( mDirProvider.GetAppDir()->Clone(getter_AddRefs(overrideini))) && NS_SUCCEEDED( overrideini->AppendNative("crashreporter-override.ini"_ns))) { #ifdef XP_WIN nsAutoString overridePathW; overrideini->GetPath(overridePathW); NS_ConvertUTF16toUTF8 overridePath(overridePathW); #else nsAutoCString overridePath; overrideini->GetNativePath(overridePath); #endif SaveWordToEnv("MOZ_CRASHREPORTER_STRINGS_OVERRIDE", overridePath); } } } else { // We might have registered a runtime exception module very early in process // startup to catch early crashes. This is before we have access to ini file // data, so unregister here if it turns out the crash reporter is disabled. CrashReporter::UnregisterRuntimeExceptionModule(); } #if defined(MOZ_SANDBOX) && defined(XP_WIN) if (mAppData->sandboxBrokerServices) { nsAutoString binDirPath; MOZ_ALWAYS_SUCCEEDS(xreBinDirectory->GetPath(binDirPath)); SandboxBroker::Initialize(mAppData->sandboxBrokerServices, binDirPath); } else { # if defined(MOZ_SANDBOX) // If we're sandboxing content and we fail to initialize, then crashing here // seems like the sensible option. if (BrowserTabsRemoteAutostart()) { MOZ_CRASH("Failed to initialize broker services, can't continue."); } # endif // Otherwise just warn for the moment, as most things will work. NS_WARNING( "Failed to initialize broker services, sandboxed processes will " "fail to start."); } #endif #ifdef XP_MACOSX // Set up ability to respond to system (Apple) events. This must occur before // ProcessUpdates to ensure that links clicked in external applications aren't // lost when updates are pending. SetupMacApplicationDelegate(&gRestartedByOS); if (EnvHasValue("MOZ_LAUNCHED_CHILD")) { // This is needed, on relaunch, to force the OS to use the "Cocoa Dock // API". Otherwise the call to ReceiveNextEvent() below will make it // use the "Carbon Dock API". For more info see bmo bug 377166. EnsureUseCocoaDockAPI(); // When the app relaunches, the original process exits. This causes // the dock tile to stop bouncing, lose the "running" triangle, and // if the tile does not permanently reside in the Dock, even disappear. // This can be confusing to the user, who is expecting the app to launch. // Calling ReceiveNextEvent without requesting any event is enough to // cause a dock tile for the child process to appear. const EventTypeSpec kFakeEventList[] = {{INT_MAX, INT_MAX}}; EventRef event; ::ReceiveNextEvent(GetEventTypeCount(kFakeEventList), kFakeEventList, kEventDurationNoWait, false, &event); } if (CheckArg("foreground")) { // The original process communicates that it was in the foreground by // adding this argument. This new process, which is taking over for // the old one, should make itself the active application. ProcessSerialNumber psn; if (::GetCurrentProcess(&psn) == noErr) ::SetFrontProcess(&psn); } #endif SaveToEnv("MOZ_LAUNCHED_CHILD="); // On Windows, the -os-restarted command line switch lets us know when we are // restarted via RegisterApplicationRestart. May be used for other OSes later. if (CheckArg("os-restarted", nullptr, CheckArgFlag::RemoveArg) == ARG_FOUND) { gRestartedByOS = true; } gRestartArgc = gArgc; gRestartArgv = (char**)malloc(sizeof(char*) * (gArgc + 1 + (override ? 2 : 0))); if (!gRestartArgv) { return 1; } int i; for (i = 0; i < gArgc; ++i) { gRestartArgv[i] = gArgv[i]; } // Add the -override argument back (it is removed automatically be CheckArg) // if there is one if (override) { gRestartArgv[gRestartArgc++] = const_cast("-override"); gRestartArgv[gRestartArgc++] = const_cast(override); } gRestartArgv[gRestartArgc] = nullptr; Maybe safeModeRequested = IsSafeModeRequested(gArgc, gArgv); if (!safeModeRequested) { return 1; } #ifdef MOZ_BACKGROUNDTASKS if (BackgroundTasks::IsBackgroundTaskMode()) { safeModeRequested = Some(false); // Remove the --backgroundtask arg now that it has been saved in // gRestartArgv. const char* tmpBackgroundTaskName = nullptr; Unused << CheckArg("backgroundtask", &tmpBackgroundTaskName, CheckArgFlag::RemoveArg); } #endif gSafeMode = safeModeRequested.value(); MaybeAddCPUMicrocodeCrashAnnotation(); CrashReporter::RegisterAnnotationBool(CrashReporter::Annotation::SafeMode, &gSafeMode); #if defined(MOZ_HAS_REMOTE) // Handle --no-remote and --new-instance command line arguments. Setup // the environment to better accommodate other components and various // restart scenarios. ar = CheckArg("no-remote"); if (ar == ARG_FOUND || EnvHasValue("MOZ_NO_REMOTE")) { mDisableRemoteClient = true; mDisableRemoteServer = true; gRestartWithoutRemote = true; // We don't want to propagate MOZ_NO_REMOTE to potential child // process. SaveToEnv("MOZ_NO_REMOTE="); } ar = CheckArg("new-instance"); if (ar == ARG_FOUND || EnvHasValue("MOZ_NEW_INSTANCE")) { mDisableRemoteClient = true; } #else // These arguments do nothing in platforms with no remoting support but we // should remove them from the command line anyway. CheckArg("no-remote"); CheckArg("new-instance"); #endif ar = CheckArg("offline"); if (ar || EnvHasValue("XRE_START_OFFLINE")) { mStartOffline = true; } // On Windows, to get working console arrangements so help/version/etc // print something, we need to initialize the native app support. rv = NS_CreateNativeAppSupport(getter_AddRefs(mNativeApp)); if (NS_FAILED(rv)) return 1; // Handle --help, --full-version and --version command line arguments. // They should return quickly, so we deal with them here. if (CheckArg("h") || CheckArg("help") || CheckArg("?")) { DumpHelp(); *aExitFlag = true; return 0; } if (CheckArg("v") || CheckArg("version")) { DumpVersion(); *aExitFlag = true; return 0; } if (CheckArg("full-version")) { DumpFullVersion(); *aExitFlag = true; return 0; } rv = XRE_InitCommandLine(gArgc, gArgv); NS_ENSURE_SUCCESS(rv, 1); return 0; } #if defined(XP_LINUX) && !defined(ANDROID) static void AnnotateLSBRelease(void*) { nsCString dist, desc, release, codename; if (widget::lsb::GetLSBRelease(dist, desc, release, codename)) { CrashReporter::AppendAppNotesToCrashReport(desc); } } #endif // defined(XP_LINUX) && !defined(ANDROID) #ifdef XP_WIN static void ReadAheadSystemDll(const wchar_t* dllName) { wchar_t dllPath[MAX_PATH]; if (ConstructSystem32Path(dllName, dllPath, MAX_PATH)) { ReadAheadLib(dllPath); } } static void ReadAheadPackagedDll(const wchar_t* dllName, const wchar_t* aGREDir) { wchar_t dllPath[MAX_PATH]; swprintf(dllPath, MAX_PATH, L"%s\\%s", aGREDir, dllName); ReadAheadLib(dllPath); } static void PR_CALLBACK ReadAheadDlls_ThreadStart(void* arg) { UniquePtr greDir(static_cast(arg)); // In Bug 1628903, we investigated which DLLs we should prefetch in // order to reduce disk I/O and improve startup on Windows machines. // Our ultimate goal is to measure the impact of these improvements on // retention (see Bug 1640087). Before we place this within a pref, // we should ensure this feature only ships to the nightly channel // and monitor results from that subset. if (greDir) { // Prefetch the DLLs shipped with firefox ReadAheadPackagedDll(L"libegl.dll", greDir.get()); ReadAheadPackagedDll(L"libGLESv2.dll", greDir.get()); ReadAheadPackagedDll(L"nssckbi.dll", greDir.get()); ReadAheadPackagedDll(L"freebl3.dll", greDir.get()); ReadAheadPackagedDll(L"softokn3.dll", greDir.get()); // Prefetch the system DLLs ReadAheadSystemDll(L"DWrite.dll"); ReadAheadSystemDll(L"D3DCompiler_47.dll"); } else { // Load DataExchange.dll and twinapi.appcore.dll for // nsWindow::EnableDragDrop ReadAheadSystemDll(L"DataExchange.dll"); ReadAheadSystemDll(L"twinapi.appcore.dll"); // Load twinapi.dll for WindowsUIUtils::UpdateTabletModeState ReadAheadSystemDll(L"twinapi.dll"); // Load explorerframe.dll for WinTaskbar::Initialize ReadAheadSystemDll(L"ExplorerFrame.dll"); // Load WinTypes.dll for nsOSHelperAppService::GetApplicationDescription ReadAheadSystemDll(L"WinTypes.dll"); } } #endif #if defined(MOZ_UPDATER) && !defined(MOZ_WIDGET_ANDROID) enum struct ShouldNotProcessUpdatesReason { DevToolsLaunching, NotAnUpdatingTask, OtherInstanceRunning, FirstStartup }; const char* ShouldNotProcessUpdatesReasonAsString( ShouldNotProcessUpdatesReason aReason) { switch (aReason) { case ShouldNotProcessUpdatesReason::DevToolsLaunching: return "DevToolsLaunching"; case ShouldNotProcessUpdatesReason::NotAnUpdatingTask: return "NotAnUpdatingTask"; case ShouldNotProcessUpdatesReason::OtherInstanceRunning: return "OtherInstanceRunning"; default: MOZ_CRASH("impossible value for ShouldNotProcessUpdatesReason"); } } Maybe ShouldNotProcessUpdates( nsXREDirProvider& aDirProvider) { // Don't process updates when launched from the installer. // It's possible for a stale update to be present in the case of a paveover; // ignore it and leave the update service to discard it. if (ARG_FOUND == CheckArgExists("first-startup")) { NS_WARNING("ShouldNotProcessUpdates(): FirstStartup"); return Some(ShouldNotProcessUpdatesReason::FirstStartup); } // Do not process updates if we're launching devtools, as evidenced by // "--chrome ..." with the browser toolbox chrome document URL. // Keep this synchronized with the value of the same name in // devtools/client/framework/browser-toolbox/Launcher.sys.mjs. Or, for bonus // points, lift this value to nsIXulRuntime or similar, so that it can be // accessed in both locations. (The prefs service isn't available at this // point so the simplest manner of sharing the value is not available to us.) const char* BROWSER_TOOLBOX_WINDOW_URL = "chrome://devtools/content/framework/browser-toolbox/window.html"; const char* chromeParam = nullptr; if (ARG_FOUND == CheckArg("chrome", &chromeParam, CheckArgFlag::None)) { if (!chromeParam || !strcmp(BROWSER_TOOLBOX_WINDOW_URL, chromeParam)) { NS_WARNING("ShouldNotProcessUpdates(): DevToolsLaunching"); return Some(ShouldNotProcessUpdatesReason::DevToolsLaunching); } } # ifdef MOZ_BACKGROUNDTASKS // Do not process updates if we're running a background task mode and another // instance is already running. This avoids periodic maintenance updating // underneath a browsing session. Maybe backgroundTasks = BackgroundTasks::GetBackgroundTasks(); if (backgroundTasks.isSome()) { // Only process updates for specific tasks: at this time, the // `backgroundupdate` task and the test-only `shouldprocessupdates` task. // // Background tasks can be sparked by Firefox instances that are shutting // down, which can cause races between the task startup trying to update and // Firefox trying to invoke the updater. This happened when converting // `pingsender` to a background task, since it is launched to send pings at // shutdown: Bug 1736373. // // We'd prefer to have this be a property of the task definition sibling to // `backgroundTaskTimeoutSec`, but when we reach this code we're well before // we can load the task JSM. if (!BackgroundTasks::IsUpdatingTaskName(backgroundTasks.ref())) { NS_WARNING("ShouldNotProcessUpdates(): NotAnUpdatingTask"); return Some(ShouldNotProcessUpdatesReason::NotAnUpdatingTask); } // At this point we have a dir provider but no XPCOM directory service. We // launch the update sync manager using that information so that it doesn't // need to ask for (and fail to find) the directory service. nsCOMPtr anAppFile; bool persistent; nsresult rv = aDirProvider.GetFile(XRE_EXECUTABLE_FILE, &persistent, getter_AddRefs(anAppFile)); if (NS_FAILED(rv) || !anAppFile) { // Strange, but not a reason to skip processing updates. return Nothing(); } auto updateSyncManager = new nsUpdateSyncManager(anAppFile); bool otherInstance = false; updateSyncManager->IsOtherInstanceRunning(&otherInstance); if (otherInstance) { NS_WARNING("ShouldNotProcessUpdates(): OtherInstanceRunning"); return Some(ShouldNotProcessUpdatesReason::OtherInstanceRunning); } } # endif return Nothing(); } #endif namespace mozilla::startup { Result, nsresult> GetIncompleteStartupFile(nsIFile* aProfLD) { nsCOMPtr crashFile; MOZ_TRY(aProfLD->Clone(getter_AddRefs(crashFile))); MOZ_TRY(crashFile->Append(FILE_STARTUP_INCOMPLETE)); return std::move(crashFile); } } // namespace mozilla::startup // Check whether the last startup attempt resulted in a crash or hang. // This is distinct from the definition of a startup crash from // nsAppStartup::TrackStartupCrashBegin. bool XREMain::CheckLastStartupWasCrash() { Result, nsresult> crashFile = GetIncompleteStartupFile(mProfLD); if (crashFile.isErr()) { return true; } // Attempt to create the incomplete startup canary file. If the file already // exists, this fails, and we know the last startup was a crash. If it // doesn't already exist, it is created, and will be removed at the end of // the startup crash detection window. AutoFDClose fd; Unused << crashFile.inspect()->OpenNSPRFileDesc( PR_WRONLY | PR_CREATE_FILE | PR_EXCL, 0666, getter_Transfers(fd)); return !fd; } /* * XRE_mainStartup - Initializes the profile and various other services. * Main() will exit early if either return value != 0 or if aExitFlag is * true. */ int XREMain::XRE_mainStartup(bool* aExitFlag) { nsresult rv; if (!aExitFlag) return 1; *aExitFlag = false; #ifdef XP_MACOSX mozilla::MacAutoreleasePool pool; #endif // Enable Telemetry IO Reporting on DEBUG, nightly and local builds, // but disable it on FUZZING builds and for ANDROID. #ifndef FUZZING # ifndef ANDROID # ifdef DEBUG mozilla::Telemetry::InitIOReporting(gAppData->xreDirectory); # else { const char* releaseChannel = MOZ_STRINGIFY(MOZ_UPDATE_CHANNEL); if (strcmp(releaseChannel, "nightly") == 0 || strcmp(releaseChannel, "default") == 0) { mozilla::Telemetry::InitIOReporting(gAppData->xreDirectory); } } # endif /* DEBUG */ # endif /* ANDROID */ #endif /* FUZZING */ #if defined(XP_WIN) // Enable the HeapEnableTerminationOnCorruption exploit mitigation. We ignore // the return code because it always returns success, although it has no // effect on Windows older than XP SP3. HeapSetInformation(NULL, HeapEnableTerminationOnCorruption, NULL, 0); #endif /* XP_WIN */ #ifdef MOZ_WIDGET_GTK // Stash startup token in owned memory because gtk_init will clear // DESKTOP_STARTUP_ID it. if (const char* v = PR_GetEnv("DESKTOP_STARTUP_ID")) { mDesktopStartupID.Assign(v); } if (const char* v = PR_GetEnv("XDG_ACTIVATION_TOKEN")) { mXDGActivationToken.Assign(v); } #endif #if defined(XP_WIN) { // Save the shortcut path before lpTitle is replaced by an AUMID, // such as by WinTaskbar STARTUPINFOW si; GetStartupInfoW(&si); if (si.dwFlags & STARTF_TITLEISAPPID) { NS_WARNING("AUMID was already set, shortcut may have been lost."); } else if ((si.dwFlags & STARTF_TITLEISLINKNAME) && si.lpTitle) { gProcessStartupShortcut.Assign(si.lpTitle); } } #endif /* XP_WIN */ #if defined(MOZ_WIDGET_GTK) // setup for private colormap. Ideally we'd like to do this // in nsAppShell::Create, but we need to get in before gtk // has been initialized to make sure everything is running // consistently. // Set program name to the one defined in application.ini. g_set_prgname(gAppData->remotingName); // Initialize GTK here for splash. # if defined(MOZ_WIDGET_GTK) && defined(MOZ_X11) // Disable XInput2 multidevice support due to focus bugginess. // See bugs 1182700, 1170342. // gdk_disable_multidevice() affects Gdk X11 backend only, // the multidevice support is always enabled on Wayland backend. const char* useXI2 = PR_GetEnv("MOZ_USE_XINPUT2"); if (!useXI2 || (*useXI2 == '0')) gdk_disable_multidevice(); # endif // Open the display ourselves instead of using gtk_init, so that we can // close it without fear that one day gtk might clean up the display it // opens. if (!gtk_parse_args(&gArgc, &gArgv)) return 1; #endif /* MOZ_WIDGET_GTK */ #ifdef FUZZING if (PR_GetEnv("FUZZER")) { *aExitFlag = true; return mozilla::fuzzerRunner->Run(&gArgc, &gArgv); } #endif if (PR_GetEnv("MOZ_RUN_GTEST")) { int result; #ifdef XP_WIN UseParentConsole(); #endif // RunGTest will only be set if we're in xul-unit if (mozilla::RunGTest) { gIsGtest = true; result = mozilla::RunGTest(&gArgc, gArgv); gIsGtest = false; } else { result = 1; printf("TEST-UNEXPECTED-FAIL | gtest | Not compiled with enable-tests\n"); } *aExitFlag = true; return result; } bool isBackgroundTaskMode = false; #ifdef MOZ_BACKGROUNDTASKS isBackgroundTaskMode = BackgroundTasks::IsBackgroundTaskMode(); #endif #ifdef MOZ_HAS_REMOTE if (gfxPlatform::IsHeadless()) { mDisableRemoteClient = true; mDisableRemoteServer = true; } #endif #ifdef MOZ_X11 // Init X11 in thread-safe mode. Must be called prior to the first call to // XOpenDisplay (called inside gdk_display_open). This is a requirement for // off main tread compositing. if (!isBackgroundTaskMode && !gfxPlatform::IsHeadless()) { XInitThreads(); } #endif #if defined(MOZ_WIDGET_GTK) if (!isBackgroundTaskMode && !gfxPlatform::IsHeadless()) { const char* display_name = nullptr; bool saveDisplayArg = false; // display_name is owned by gdk. display_name = gdk_get_display_arg_name(); bool waylandEnabled = IsWaylandEnabled(); # ifdef MOZ_WAYLAND if (!display_name) { auto* proxyEnv = getenv("MOZ_DISABLE_WAYLAND_PROXY"); bool disableWaylandProxy = proxyEnv && *proxyEnv; if (!disableWaylandProxy && XRE_IsParentProcess() && waylandEnabled) { # ifdef MOZ_LOGGING if (MOZ_LOG_TEST(gWidgetWaylandLog, mozilla::LogLevel::Debug)) { WaylandProxy::SetVerbose(true); } # endif gWaylandProxy = WaylandProxy::Create(); if (gWaylandProxy) { gWaylandProxy->RunThread(); } } } # endif // if --display argument is given make sure it's // also passed to ContentChild::Init() by MOZ_GDK_DISPLAY. if (display_name) { SaveWordToEnv("MOZ_GDK_DISPLAY", nsDependentCString(display_name)); saveDisplayArg = true; } // On Wayland disabled builds read X11 DISPLAY env exclusively // and don't care about different displays. if (!waylandEnabled && !display_name) { display_name = PR_GetEnv("DISPLAY"); if (!display_name) { PR_fprintf(PR_STDERR, "Error: no DISPLAY environment variable specified\n"); return 1; } } if (display_name) { GdkDisplay* disp = gdk_display_open(display_name); if (!disp) { PR_fprintf(PR_STDERR, "Error: cannot open display: %s\n", display_name); return 1; } if (saveDisplayArg) { if (GdkIsX11Display(disp)) { SaveWordToEnv("DISPLAY", nsDependentCString(display_name)); } else if (GdkIsWaylandDisplay(disp)) { SaveWordToEnv("WAYLAND_DISPLAY", nsDependentCString(display_name)); } } } else { gdk_display_manager_open_display(gdk_display_manager_get(), nullptr); } # if defined(MOZ_WAYLAND) // We want to use proxy for main connection only so // restore original Wayland display for next potential Wayland connections // from gfx probe code and so on. if (gWaylandProxy) { gWaylandProxy->RestoreWaylandDisplay(); } if (waylandEnabled && PR_GetEnv("WAYLAND_DISPLAY") && GdkIsX11Display()) { // Gtk somehow switched to X11 display but we want Wayland. // It may happen if compositor response is missig or it's slow // or WAYLAND_DISPLAY is wrong. In such case throw warning but // run with X11. Output(true, "Error: Failed to open Wayland display, fallback to X11. " "WAYLAND_DISPLAY='%s' DISPLAY='%s'\n", PR_GetEnv("WAYLAND_DISPLAY"), PR_GetEnv("DISPLAY")); // We need to unset WAYLAND_DISPLAY. Gfx probe code doesn't have fallback // to X11 and we'll end with Browser running SW rendering only then. g_unsetenv("WAYLAND_DISPLAY"); gWaylandProxy = nullptr; } # endif if (!gdk_display_get_default()) { Output(true, "Error: we don't have any display, WAYLAND_DISPLAY='%s' " "DISPLAY='%s'\n", PR_GetEnv("WAYLAND_DISPLAY"), PR_GetEnv("DISPLAY")); return 1; } // Check that Wayland only and X11 only builds // use appropriate displays. # if defined(MOZ_WAYLAND) && !defined(MOZ_X11) if (!GdkIsWaylandDisplay()) { Output(true, "Wayland only build is missig Wayland display!\n"); return 1; } # endif # if !defined(MOZ_WAYLAND) && defined(MOZ_X11) if (!GdkIsX11Display()) { Output(true, "X11 only build is missig X11 display!\n"); return 1; } # endif } #endif #if defined(MOZ_HAS_REMOTE) // handle --remote now that xpcom is fired up mRemoteService = new nsRemoteService(gAppData->remotingName); if (mRemoteService && !mDisableRemoteServer) { mRemoteService->LockStartup(); gRemoteService = mRemoteService; } #endif #if defined(MOZ_WIDGET_GTK) g_set_application_name(mAppData->name); #endif /* defined(MOZ_WIDGET_GTK) */ #ifdef MOZ_X11 // Do this after initializing GDK, or GDK will install its own handler. XRE_InstallX11ErrorHandler(); #endif // Call the code to install our handler #ifdef MOZ_JPROF setupProfilingStuff(); #endif bool canRun = false; rv = mNativeApp->Start(&canRun); if (NS_FAILED(rv) || !canRun) { return 1; } #ifdef MOZ_WIDGET_GTK // startup token might be cleared now, we recover it in case we need a // restart. if (!mDesktopStartupID.IsEmpty()) { // Leak it with extreme prejudice! PR_SetEnv(ToNewCString("DESKTOP_STARTUP_ID="_ns + mDesktopStartupID)); } if (!mXDGActivationToken.IsEmpty()) { // Leak it with extreme prejudice! PR_SetEnv(ToNewCString("XDG_ACTIVATION_TOKEN="_ns + mXDGActivationToken)); } #endif // Support exiting early for testing startup sequence. Bug 1360493 if (CheckArg("test-launch-without-hang")) { *aExitFlag = true; return 0; } mProfileSvc = NS_GetToolkitProfileService(); if (!mProfileSvc) { // We failed to choose or create profile - notify user and quit ProfileMissingDialog(mNativeApp); return 1; } bool wasDefaultSelection; nsCOMPtr profile; rv = SelectProfile(mProfileSvc, mNativeApp, getter_AddRefs(mProfD), getter_AddRefs(mProfLD), getter_AddRefs(profile), &wasDefaultSelection); if (rv == NS_ERROR_LAUNCHED_CHILD_PROCESS || rv == NS_ERROR_ABORT) { *aExitFlag = true; return 0; } if (NS_FAILED(rv)) { // We failed to choose or create profile - notify user and quit ProfileMissingDialog(mNativeApp); return 1; } #if defined(MOZ_HAS_REMOTE) if (mRemoteService) { // We want a unique profile name to identify the remote instance. nsCString profileName; if (profile) { rv = profile->GetName(profileName); } if (!profile || NS_FAILED(rv) || profileName.IsEmpty()) { // Couldn't get a name from the profile. Use the directory name? nsString leafName; rv = mProfD->GetLeafName(leafName); if (NS_SUCCEEDED(rv)) { CopyUTF16toUTF8(leafName, profileName); } } mRemoteService->SetProfile(profileName); if (!mDisableRemoteClient) { // Try to remote the entire command line. If this fails, start up // normally. # ifdef MOZ_WIDGET_GTK const auto& startupToken = GdkIsWaylandDisplay() ? mXDGActivationToken : mDesktopStartupID; # else const nsCString startupToken; # endif RemoteResult rr = mRemoteService->StartClient( startupToken.IsEmpty() ? nullptr : startupToken.get()); if (rr == REMOTE_FOUND) { *aExitFlag = true; mRemoteService->UnlockStartup(); return 0; } if (rr == REMOTE_ARG_BAD) { mRemoteService->UnlockStartup(); return 1; } } } #endif #if defined(MOZ_UPDATER) && !defined(MOZ_WIDGET_ANDROID) # ifdef XP_WIN { // When automatically restarting for a staged background update // we want the child process to wait here so that the updater // does not register two instances running and use that as a // reason to not process updates. This function requires having // -restart-pid in the command line to function properly. // Ensure we keep -restart-pid if we are running tests if (ARG_FOUND == CheckArgExists("restart-pid") && !CheckArg("test-only-automatic-restart-no-wait")) { // We're not testing and can safely remove it now and read the pid. const char* restartPidString = nullptr; CheckArg("restart-pid", &restartPidString, CheckArgFlag::RemoveArg); // pid should be first parameter following -restart-pid. uint32_t pid = nsDependentCString(restartPidString).ToInteger(&rv, 10U); if (NS_SUCCEEDED(rv) && pid > 0) { printf_stderr( "*** MaybeWaitForProcessExit: launched pidDWORD = %u ***\n", pid); RefPtr updater = new nsUpdateProcessor(); if (NS_FAILED( updater->WaitForProcessExit(pid, MAYBE_WAIT_TIMEOUT_MS))) { NS_WARNING("Failed to MaybeWaitForProcessExit."); } } else { NS_WARNING("Failed to parse pid from -restart-pid."); } } } # endif Maybe shouldNotProcessUpdatesReason = ShouldNotProcessUpdates(mDirProvider); if (shouldNotProcessUpdatesReason.isNothing()) { // Check for and process any available updates nsCOMPtr updRoot; bool persistent; rv = mDirProvider.GetFile(XRE_UPDATE_ROOT_DIR, &persistent, getter_AddRefs(updRoot)); // XRE_UPDATE_ROOT_DIR may fail. Fallback to appDir if failed if (NS_FAILED(rv)) { updRoot = mDirProvider.GetAppDir(); } // If the MOZ_TEST_PROCESS_UPDATES environment variable already exists, then // we are being called from the callback application. if (EnvHasValue("MOZ_TEST_PROCESS_UPDATES")) { // If the caller has asked us to log our arguments, do so. This is used // to make sure that the maintenance service successfully launches the // callback application. const char* logFile = nullptr; if (ARG_FOUND == CheckArg("dump-args", &logFile)) { FILE* logFP = fopen(logFile, "wb"); if (logFP) { for (int i = 1; i < gRestartArgc; ++i) { fprintf(logFP, "%s\n", gRestartArgv[i]); } fclose(logFP); } } *aExitFlag = true; return 0; } // Support for processing an update and exiting. The // MOZ_TEST_PROCESS_UPDATES environment variable will be part of the // updater's environment and the application that is relaunched by the // updater. When the application is relaunched by the updater it will be // removed below and the application will exit. if (CheckArg("test-process-updates")) { SaveToEnv("MOZ_TEST_PROCESS_UPDATES=1"); } nsCOMPtr exeFile, exeDir; rv = mDirProvider.GetFile(XRE_EXECUTABLE_FILE, &persistent, getter_AddRefs(exeFile)); NS_ENSURE_SUCCESS(rv, 1); rv = exeFile->GetParent(getter_AddRefs(exeDir)); NS_ENSURE_SUCCESS(rv, 1); ProcessUpdates(mDirProvider.GetGREDir(), exeDir, updRoot, gRestartArgc, gRestartArgv, mAppData->version); if (EnvHasValue("MOZ_TEST_PROCESS_UPDATES")) { SaveToEnv("MOZ_TEST_PROCESS_UPDATES="); *aExitFlag = true; return 0; } } else { if (CheckArg("test-process-updates") || EnvHasValue("MOZ_TEST_PROCESS_UPDATES")) { // Support for testing *not* processing an update. The launched process // can witness this environment variable and conclude that its runtime // environment resulted in not processing updates. SaveToEnv(nsPrintfCString( "MOZ_TEST_PROCESS_UPDATES=ShouldNotProcessUpdates(): %s", ShouldNotProcessUpdatesReasonAsString( shouldNotProcessUpdatesReason.value())) .get()); } } #endif // We now know there is no existing instance using the selected profile. If // the profile wasn't selected by specific command line arguments and the // user has chosen to show the profile manager on startup then do that. if (wasDefaultSelection) { bool useSelectedProfile; rv = mProfileSvc->GetStartWithLastProfile(&useSelectedProfile); NS_ENSURE_SUCCESS(rv, 1); if (!useSelectedProfile) { rv = ShowProfileManager(mProfileSvc, mNativeApp); if (rv == NS_ERROR_LAUNCHED_CHILD_PROCESS || rv == NS_ERROR_ABORT) { *aExitFlag = true; return 0; } if (NS_FAILED(rv)) { return 1; } } } // We always want to lock the profile even if we're actually going to reset // it later. rv = LockProfile(mNativeApp, mProfD, mProfLD, profile, getter_AddRefs(mProfileLock)); if (rv == NS_ERROR_LAUNCHED_CHILD_PROCESS || rv == NS_ERROR_ABORT) { *aExitFlag = true; return 0; } else if (NS_FAILED(rv)) { return 1; } if (gDoProfileReset) { if (EnvHasValue("MOZ_RESET_PROFILE_RESTART")) { SaveToEnv("MOZ_RESET_PROFILE_RESTART="); // We only want to restore the previous session if the profile refresh was // triggered by user. And if it was a user-triggered profile refresh // through, say, the safeMode dialog or the troubleshooting page, the // MOZ_RESET_PROFILE_RESTART env variable would be set. Hence we set // MOZ_RESET_PROFILE_MIGRATE_SESSION here so that Firefox profile migrator // would migrate old session data later. SaveToEnv("MOZ_RESET_PROFILE_MIGRATE_SESSION=1"); } // Unlock the source profile. mProfileLock->Unlock(); // If we're resetting a profile, create a new one and use it to startup. gResetOldProfile = profile; rv = mProfileSvc->CreateResetProfile(getter_AddRefs(profile)); if (NS_SUCCEEDED(rv)) { rv = profile->GetRootDir(getter_AddRefs(mProfD)); NS_ENSURE_SUCCESS(rv, 1); SaveFileToEnv("XRE_PROFILE_PATH", mProfD); rv = profile->GetLocalDir(getter_AddRefs(mProfLD)); NS_ENSURE_SUCCESS(rv, 1); SaveFileToEnv("XRE_PROFILE_LOCAL_PATH", mProfLD); // Lock the new profile rv = LockProfile(mNativeApp, mProfD, mProfLD, profile, getter_AddRefs(mProfileLock)); if (rv == NS_ERROR_LAUNCHED_CHILD_PROCESS || rv == NS_ERROR_ABORT) { *aExitFlag = true; return 0; } else if (NS_FAILED(rv)) { return 1; } } else { NS_WARNING("Profile reset failed."); return 1; } } gProfileLock = mProfileLock; nsAutoCString version; BuildVersion(version); #ifdef TARGET_OS_ABI constexpr auto osABI = nsLiteralCString{TARGET_OS_ABI}; #else // No TARGET_XPCOM_ABI, but at least the OS is known constexpr auto osABI = nsLiteralCString{OS_TARGET "_UNKNOWN"}; #endif // Check for version compatibility with the last version of the app this // profile was started with. The format of the version stamp is defined // by the BuildVersion function. // Also check to see if something has happened to invalidate our // fastload caches, like an app upgrade. // If we see .purgecaches, that means someone did a make. // Re-register components to catch potential changes. nsCOMPtr flagFile; if (mAppData->directory) { Unused << mAppData->directory->Clone(getter_AddRefs(flagFile)); } if (flagFile) { flagFile->AppendNative(FILE_INVALIDATE_CACHES); } bool cachesOK; bool isDowngrade; nsCString lastVersion; bool versionOK = CheckCompatibility( mProfD, version, osABI, mDirProvider.GetGREDir(), mAppData->directory, flagFile, &cachesOK, &isDowngrade, lastVersion); MOZ_RELEASE_ASSERT(!cachesOK || lastVersion.Equals(version), "Caches cannot be good if the version has changed."); #ifdef MOZ_BLOCK_PROFILE_DOWNGRADE // The argument check must come first so the argument is always removed from // the command line regardless of whether this is a downgrade or not. if (!CheckArg("allow-downgrade") && isDowngrade && !EnvHasValue("MOZ_ALLOW_DOWNGRADE")) { rv = CheckDowngrade(mProfD, mNativeApp, mProfileSvc, lastVersion); if (rv == NS_ERROR_LAUNCHED_CHILD_PROCESS || rv == NS_ERROR_ABORT) { *aExitFlag = true; return 0; } } #endif rv = mDirProvider.SetProfile(mProfD, mProfLD); NS_ENSURE_SUCCESS(rv, 1); //////////////////////// NOW WE HAVE A PROFILE //////////////////////// mozilla::Telemetry::SetProfileDir(mProfD); if (mAppData->flags & NS_XRE_ENABLE_CRASH_REPORTER) { MakeOrSetMinidumpPath(mProfD); } CrashReporter::SetProfileDirectory(mProfD); #ifdef MOZ_ASAN_REPORTER // In ASan reporter builds, we need to set ASan's log_path as early as // possible, so it dumps its errors into files there instead of using // the default stderr location. Since this is crucial for ASan reporter // to work at all (and we don't want people to use a non-functional // ASan reporter build), all failures while setting log_path are fatal. setASanReporterPath(mProfD); // Export to env for child processes SaveFileToEnv("ASAN_REPORTER_PATH", mProfD); #endif bool lastStartupWasCrash = CheckLastStartupWasCrash(); CrashReporter::RecordAnnotationBool( CrashReporter::Annotation::LastStartupWasCrash, lastStartupWasCrash); if (CheckArg("purgecaches") || PR_GetEnv("MOZ_PURGE_CACHES") || lastStartupWasCrash || gSafeMode) { cachesOK = false; } CrashReporter::RecordAnnotationBool( CrashReporter::Annotation::StartupCacheValid, cachesOK && versionOK); // Every time a profile is loaded by a build with a different version, // it updates the compatibility.ini file saying what version last wrote // the fastload caches. On subsequent launches if the version matches, // there is no need for re-registration. If the user loads the same // profile in different builds the component registry must be // re-generated to prevent mysterious component loading failures. // bool startupCacheValid = true; if (!cachesOK || !versionOK) { QuotaManager::InvalidateQuotaCache(); startupCacheValid = RemoveComponentRegistries(mProfD, mProfLD, false); // Rewrite compatibility.ini to match the current build. The next run // should attempt to invalidate the caches if either this run is safe mode // or the attempt to invalidate the caches this time failed. WriteVersion(mProfD, version, osABI, mDirProvider.GetGREDir(), mAppData->directory, gSafeMode || !startupCacheValid); } if (!startupCacheValid) StartupCache::IgnoreDiskCache(); if (flagFile) { flagFile->Remove(true); } // Flush any pending page load events. mozilla::glean_pings::Pageload.Submit("startup"_ns); if (!isBackgroundTaskMode) { #ifdef USE_GLX_TEST GfxInfo::FireGLXTestProcess(); #endif #ifdef MOZ_WAYLAND // Make sure we have wayland connection for main thread. // It's used as template to create display connections // for different threads. if (IsWaylandEnabled()) { MOZ_UNUSED(WaylandDisplayGet()); } #endif #ifdef MOZ_WIDGET_GTK nsAppShell::InstallTermSignalHandler(); #endif } return 0; } #if defined(MOZ_SANDBOX) void AddSandboxAnnotations() { CrashReporter::RecordAnnotationU32( CrashReporter::Annotation::ContentSandboxLevel, GetEffectiveContentSandboxLevel()); CrashReporter::RecordAnnotationU32(CrashReporter::Annotation::GpuSandboxLevel, GetEffectiveGpuSandboxLevel()); // Include whether or not this instance is capable of content sandboxing bool sSandboxCapable = false; # if defined(XP_WIN) // All supported Windows versions support some level of content sandboxing sSandboxCapable = true; # elif defined(XP_MACOSX) // All supported OS X versions are capable sSandboxCapable = true; # elif defined(XP_LINUX) sSandboxCapable = SandboxInfo::Get().CanSandboxContent(); # elif defined(__OpenBSD__) sSandboxCapable = true; StartOpenBSDSandbox(GeckoProcessType_Default); # endif CrashReporter::RecordAnnotationBool( CrashReporter::Annotation::ContentSandboxCapable, sSandboxCapable); } #endif /* MOZ_SANDBOX */ /* * XRE_mainRun - Command line startup, profile migration, and * the calling of appStartup->Run(). */ nsresult XREMain::XRE_mainRun() { nsresult rv = NS_OK; NS_ASSERTION(mScopedXPCOM, "Scoped xpcom not initialized."); #if defined(XP_WIN) RefPtr dllServices(mozilla::DllServices::Get()); dllServices->StartUntrustedModulesProcessor(false); auto dllServicesDisable = MakeScopeExit([&dllServices]() { dllServices->DisableFull(); }); mozilla::mscom::InitProfilerMarkers(); #endif // defined(XP_WIN) // We need the appStartup pointer to span multiple scopes, so we declare // it here. nsCOMPtr appStartup; // Ditto with the command line. nsCOMPtr cmdLine; { #ifdef XP_MACOSX // In this scope, create an autorelease pool that will leave scope with // it just before entering our event loop. mozilla::MacAutoreleasePool pool; #endif rv = mScopedXPCOM->SetWindowCreator(mNativeApp); NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE); // tell the crash reporter to also send the release channel nsCOMPtr prefs = do_GetService("@mozilla.org/preferences-service;1", &rv); if (NS_SUCCEEDED(rv)) { nsCOMPtr defaultPrefBranch; rv = prefs->GetDefaultBranch(nullptr, getter_AddRefs(defaultPrefBranch)); if (NS_SUCCEEDED(rv)) { nsAutoCString sval; rv = defaultPrefBranch->GetCharPref("app.update.channel", sval); if (NS_SUCCEEDED(rv)) { CrashReporter::RecordAnnotationNSCString( CrashReporter::Annotation::ReleaseChannel, sval); } } } // Needs to be set after xpcom initialization. bool includeContextHeap = Preferences::GetBool( "toolkit.crashreporter.include_context_heap", false); CrashReporter::SetIncludeContextHeap(includeContextHeap); #if defined(XP_LINUX) && !defined(ANDROID) PR_CreateThread(PR_USER_THREAD, AnnotateLSBRelease, 0, PR_PRIORITY_LOW, PR_GLOBAL_THREAD, PR_UNJOINABLE_THREAD, 0); #endif if (mStartOffline) { nsCOMPtr io( do_GetService("@mozilla.org/network/io-service;1")); NS_ENSURE_TRUE(io, NS_ERROR_FAILURE); io->SetManageOfflineStatus(false); io->SetOffline(true); } #ifdef XP_WIN mozilla::DllPrefetchExperimentRegistryInfo prefetchRegInfo; mozilla::AlteredDllPrefetchMode dllPrefetchMode = prefetchRegInfo.GetAlteredDllPrefetchMode(); if (!PR_GetEnv("XRE_NO_DLL_READAHEAD") && dllPrefetchMode != mozilla::AlteredDllPrefetchMode::NoPrefetch) { nsCOMPtr greDir = mDirProvider.GetGREDir(); nsAutoString path; rv = greDir->GetPath(path); if (NS_SUCCEEDED(rv)) { PRThread* readAheadThread; wchar_t* pathRaw; // We use the presence of a path argument inside the thread to determine // which list of Dlls to use. The old list does not need access to the // GRE dir, so the path argument is set to a null pointer. if (dllPrefetchMode == mozilla::AlteredDllPrefetchMode::OptimizedPrefetch) { pathRaw = new wchar_t[MAX_PATH]; wcscpy_s(pathRaw, MAX_PATH, path.get()); } else { pathRaw = nullptr; } readAheadThread = PR_CreateThread( PR_USER_THREAD, ReadAheadDlls_ThreadStart, (void*)pathRaw, PR_PRIORITY_NORMAL, PR_GLOBAL_THREAD, PR_UNJOINABLE_THREAD, 0); if (readAheadThread == NULL) { delete[] pathRaw; } } } #endif if (gDoMigration) { nsCOMPtr file; mDirProvider.GetAppDir()->Clone(getter_AddRefs(file)); file->AppendNative("override.ini"_ns); nsINIParser parser; nsresult rv = parser.Init(file); // if override.ini doesn't exist, also check for distribution.ini if (NS_FAILED(rv)) { bool persistent; mDirProvider.GetFile(XRE_APP_DISTRIBUTION_DIR, &persistent, getter_AddRefs(file)); file->AppendNative("distribution.ini"_ns); rv = parser.Init(file); } if (NS_SUCCEEDED(rv)) { nsAutoCString buf; rv = parser.GetString("XRE", "EnableProfileMigrator", buf); if (NS_SUCCEEDED(rv)) { if (buf[0] == '0' || buf[0] == 'f' || buf[0] == 'F') { gDoMigration = false; } } } } // We'd like to initialize the JSContext *after* reading the user prefs. // Unfortunately that's not possible if we have to do profile migration // because that requires us to execute JS before reading user prefs. // Restarting the browser after profile migration would fix this. See // bug 1592523. bool initializedJSContext = false; { // Profile Migration if (mAppData->flags & NS_XRE_ENABLE_PROFILE_MIGRATOR && gDoMigration) { gDoMigration = false; xpc::InitializeJSContext(); initializedJSContext = true; nsCOMPtr pm( do_CreateInstance(NS_PROFILEMIGRATOR_CONTRACTID)); if (pm) { nsAutoCString aKey; nsAutoCString aName; if (gDoProfileReset) { // Automatically migrate from the current application if we just // reset the profile. aKey = MOZ_APP_NAME; gResetOldProfile->GetName(aName); } #ifdef XP_MACOSX // Necessary for migration wizard to be accessible. InitializeMacApp(); #endif pm->Migrate(&mDirProvider, aKey, aName); } } if (gDoProfileReset) { if (!initializedJSContext) { xpc::InitializeJSContext(); initializedJSContext = true; } nsresult backupCreated = ProfileResetCleanup(mProfileSvc, gResetOldProfile); if (NS_FAILED(backupCreated)) { NS_WARNING("Could not cleanup the profile that was reset"); } } } // Initialize user preferences before notifying startup observers so they're // ready in time for early consumers, such as the component loader. mDirProvider.InitializeUserPrefs(); // Now that all (user) prefs have been loaded we can initialize the main // thread's JSContext. if (!initializedJSContext) { xpc::InitializeJSContext(); } // Finally, now that JS has been initialized, we can finish pref loading. // This needs to happen after JS and XPConnect initialization because // AutoConfig files require JS execution. Note that this means AutoConfig // files can't override JS engine start-up prefs. mDirProvider.FinishInitializingUserPrefs(); #if defined(MOZ_SANDBOX) && defined(XP_WIN) // Now that we have preferences and the directory provider, we can // finish initializing SandboxBroker. This must happen before the GFX // platform is initialized (which will launch the GPU process), which // occurs when the "app-startup" category is started up below // // After this completes, we are ready to launch sandboxed processes mozilla::SandboxBroker::GeckoDependentInitialize(); #endif nsCOMPtr workingDir; rv = NS_GetSpecialDirectory(NS_OS_CURRENT_WORKING_DIR, getter_AddRefs(workingDir)); if (NS_FAILED(rv)) { // No working dir? This can happen if it gets deleted before we start. workingDir = nullptr; } cmdLine = new nsCommandLine(); rv = cmdLine->Init(gArgc, gArgv, workingDir, nsICommandLine::STATE_INITIAL_LAUNCH); NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE); // "app-startup" is the name of both the category and the event NS_CreateServicesFromCategory("app-startup", cmdLine, "app-startup", nullptr); appStartup = components::AppStartup::Service(); NS_ENSURE_TRUE(appStartup, NS_ERROR_FAILURE); mDirProvider.DoStartup(); #ifdef XP_WIN // It needs to be called on the main thread because it has to use // nsObserverService. EnsureWin32kInitialized(); #endif // As FilePreferences need the profile directory, we must initialize right // here. mozilla::FilePreferences::InitDirectoriesAllowlist(); mozilla::FilePreferences::InitPrefs(); nsCString userAgentLocale; LocaleService::GetInstance()->GetAppLocaleAsBCP47(userAgentLocale); CrashReporter::RecordAnnotationNSCString( CrashReporter::Annotation::useragent_locale, userAgentLocale); if (!AppShutdown::IsInOrBeyond(ShutdownPhase::AppShutdownConfirmed)) { /* Special-case services that need early access to the command line. */ nsCOMPtr obsService = mozilla::services::GetObserverService(); if (obsService) { obsService->NotifyObservers(cmdLine, "command-line-startup", nullptr); } } #ifdef XP_WIN // Hack to sync up the various environment storages. XUL_APP_FILE is special // in that it comes from a different CRT (firefox.exe's static-linked copy). // Ugly details in http://bugzil.la/1175039#c27 char appFile[MAX_PATH]; if (GetEnvironmentVariableA("XUL_APP_FILE", appFile, sizeof(appFile))) { SmprintfPointer saved = mozilla::Smprintf("XUL_APP_FILE=%s", appFile); // We intentionally leak the string here since it is required by // PR_SetEnv. PR_SetEnv(saved.release()); } #endif mozilla::AppShutdown::SaveEnvVarsForPotentialRestart(); // clear out any environment variables which may have been set // during the relaunch process now that we know we won't be relaunching. SaveToEnv("XRE_PROFILE_PATH="); SaveToEnv("XRE_PROFILE_LOCAL_PATH="); SaveToEnv("XRE_START_OFFLINE="); SaveToEnv("XUL_APP_FILE="); SaveToEnv("XRE_BINARY_PATH="); SaveToEnv("XRE_RESTARTED_BY_PROFILE_MANAGER="); if (!AppShutdown::IsInOrBeyond(ShutdownPhase::AppShutdownConfirmed)) { // Don't create the hidden window during startup on // platforms that don't always need it. #ifdef XP_MACOSX bool lazyHiddenWindow = false; #else bool lazyHiddenWindow = true; #endif #ifdef MOZ_BACKGROUNDTASKS if (BackgroundTasks::IsBackgroundTaskMode()) { // Background tasks aren't going to load a chrome XUL document. lazyHiddenWindow = true; } #endif if (!lazyHiddenWindow) { rv = appStartup->CreateHiddenWindow(); NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE); } #ifdef XP_WIN Preferences::RegisterCallbackAndCall( RegisterApplicationRestartChanged, PREF_WIN_REGISTER_APPLICATION_RESTART); SetupAlteredPrefetchPref(); SetupSkeletonUIPrefs(); # if defined(MOZ_LAUNCHER_PROCESS) SetupLauncherProcessPref(); # endif // defined(MOZ_LAUNCHER_PROCESS) # if defined(MOZ_DEFAULT_BROWSER_AGENT) # if defined(MOZ_BACKGROUNDTASKS) // The backgroundtask profile is not a browsing profile, let alone the new // default profile, so don't mirror its properties into the registry. if (!BackgroundTasks::IsBackgroundTaskMode()) # endif // defined(MOZ_BACKGROUNDTASKS) { Preferences::RegisterCallbackAndCall( &OnDefaultAgentTelemetryPrefChanged, kPrefHealthReportUploadEnabled); Preferences::RegisterCallbackAndCall( &OnDefaultAgentTelemetryPrefChanged, kPrefDefaultAgentEnabled); Preferences::RegisterCallbackAndCall( &OnDefaultAgentRemoteSettingsPrefChanged, kPrefServicesSettingsServer); Preferences::RegisterCallbackAndCall( &OnSetDefaultBrowserUserChoicePrefChanged, kPrefSetDefaultBrowserUserChoicePref); SetDefaultAgentLastRunTime(); } # endif // defined(MOZ_DEFAULT_BROWSER_AGENT) #endif #if defined(MOZ_WIDGET_GTK) // Clear the environment variables so they won't be inherited by child // processes and confuse things. for (const auto& name : kStartupTokenNames) { g_unsetenv(name.get()); } #endif #ifdef XP_MACOSX InitializeMacApp(); // we re-initialize the command-line service and do appleevents munging // after we are sure that we're not restarting cmdLine = new nsCommandLine(); char** tempArgv = static_cast(malloc(gArgc * sizeof(char*))); for (int i = 0; i < gArgc; i++) { tempArgv[i] = strdup(gArgv[i]); } CommandLineServiceMac::SetupMacCommandLine(gArgc, tempArgv, false); rv = cmdLine->Init(gArgc, tempArgv, workingDir, nsICommandLine::STATE_INITIAL_LAUNCH); free(tempArgv); NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE); # ifdef MOZILLA_OFFICIAL // Check if we're running from a DMG or an app translocated location and // allow the user to install to the Applications directory. if (MacRunFromDmgUtils::MaybeInstallAndRelaunch()) { bool userAllowedQuit = true; appStartup->Quit(nsIAppStartup::eForceQuit, 0, &userAllowedQuit); } # endif #endif nsCOMPtr obsService = mozilla::services::GetObserverService(); if (obsService) obsService->NotifyObservers(nullptr, "final-ui-startup", nullptr); (void)appStartup->DoneStartingUp(); CrashReporter::RecordAnnotationBool( CrashReporter::Annotation::StartupCrash, false); AppShutdown::IsInOrBeyond(ShutdownPhase::AppShutdownConfirmed); } if (!AppShutdown::IsInOrBeyond(ShutdownPhase::AppShutdownConfirmed)) { rv = cmdLine->Run(); NS_ENSURE_SUCCESS_LOG(rv, NS_ERROR_FAILURE); } if (!AppShutdown::IsInOrBeyond(ShutdownPhase::AppShutdownConfirmed)) { #if defined(MOZ_HAS_REMOTE) // if we have X remote support, start listening for requests on the // proxy window. if (mRemoteService && !mDisableRemoteServer) { mRemoteService->StartupServer(); mRemoteService->UnlockStartup(); gRemoteService = nullptr; } #endif /* MOZ_WIDGET_GTK */ mNativeApp->Enable(); } #ifdef MOZ_INSTRUMENT_EVENT_LOOP if (PR_GetEnv("MOZ_INSTRUMENT_EVENT_LOOP")) { bool logToConsole = true; mozilla::InitEventTracing(logToConsole); } #endif /* MOZ_INSTRUMENT_EVENT_LOOP */ // Send Telemetry about Gecko version and buildid mozilla::glean::gecko::version.Set(nsDependentCString(gAppData->version)); mozilla::glean::gecko::build_id.Set(nsDependentCString(gAppData->buildID)); #if defined(MOZ_SANDBOX) && defined(XP_LINUX) // If we're on Linux, we now have information about the OS capabilities // available to us. SandboxInfo sandboxInfo = SandboxInfo::Get(); Telemetry::Accumulate(Telemetry::SANDBOX_HAS_SECCOMP_BPF, sandboxInfo.Test(SandboxInfo::kHasSeccompBPF)); Telemetry::Accumulate(Telemetry::SANDBOX_HAS_SECCOMP_TSYNC, sandboxInfo.Test(SandboxInfo::kHasSeccompTSync)); Telemetry::Accumulate( Telemetry::SANDBOX_HAS_USER_NAMESPACES_PRIVILEGED, sandboxInfo.Test(SandboxInfo::kHasPrivilegedUserNamespaces)); Telemetry::Accumulate(Telemetry::SANDBOX_HAS_USER_NAMESPACES, sandboxInfo.Test(SandboxInfo::kHasUserNamespaces)); Telemetry::Accumulate(Telemetry::SANDBOX_CONTENT_ENABLED, sandboxInfo.Test(SandboxInfo::kEnabledForContent)); Telemetry::Accumulate(Telemetry::SANDBOX_MEDIA_ENABLED, sandboxInfo.Test(SandboxInfo::kEnabledForMedia)); CrashReporter::RecordAnnotationU32( CrashReporter::Annotation::ContentSandboxCapabilities, sandboxInfo.AsInteger()); #endif /* MOZ_SANDBOX && XP_LINUX */ #if defined(XP_WIN) LauncherResult isAdminWithoutUac = IsAdminWithoutUac(); if (isAdminWithoutUac.isOk()) { Telemetry::ScalarSet( Telemetry::ScalarID::OS_ENVIRONMENT_IS_ADMIN_WITHOUT_UAC, isAdminWithoutUac.unwrap()); } #endif /* XP_WIN */ #if defined(MOZ_SANDBOX) AddSandboxAnnotations(); #endif /* MOZ_SANDBOX */ mProfileSvc->CompleteStartup(); } #ifdef MOZ_BACKGROUNDTASKS if (BackgroundTasks::IsBackgroundTaskMode()) { // In background task mode, we don't fire various delayed initialization // notifications, which in the regular browser is how startup crash tracking // is marked as finished. Here, getting this far means we don't have a // startup crash. rv = appStartup->TrackStartupCrashEnd(); NS_ENSURE_SUCCESS(rv, rv); // We never open a window, but don't want to exit immediately. rv = appStartup->EnterLastWindowClosingSurvivalArea(); NS_ENSURE_SUCCESS(rv, rv); // Avoid some small differences in initialization order across platforms. nsCOMPtr powerManagerService = do_GetService(POWERMANAGERSERVICE_CONTRACTID); nsCOMPtr stringBundleService = do_GetService(NS_STRINGBUNDLE_CONTRACTID); rv = BackgroundTasks::RunBackgroundTask(cmdLine); NS_ENSURE_SUCCESS(rv, rv); } #endif { rv = appStartup->Run(); if (NS_FAILED(rv)) { NS_ERROR("failed to run appstartup"); gLogConsoleErrors = true; } } return rv; } #if defined(MOZ_WIDGET_ANDROID) static already_AddRefed GreOmniPath(int argc, char** argv) { nsresult rv; const char* path = nullptr; ArgResult ar = CheckArg(argc, argv, "greomni", &path, CheckArgFlag::None); if (ar == ARG_BAD) { PR_fprintf(PR_STDERR, "Error: argument --greomni requires a path argument\n"); return nullptr; } if (!path) return nullptr; nsCOMPtr greOmni; rv = XRE_GetFileFromPath(path, getter_AddRefs(greOmni)); if (NS_FAILED(rv)) { PR_fprintf(PR_STDERR, "Error: argument --greomni requires a valid path\n"); return nullptr; } return greOmni.forget(); } #endif /* * XRE_main - A class based main entry point used by most platforms. * Note that on OSX, aAppData->xreDirectory will point to * .app/Contents/Resources. */ int XREMain::XRE_main(int argc, char* argv[], const BootstrapConfig& aConfig) { gArgc = argc; gArgv = argv; ScopedLogging log; mozilla::LogModule::Init(gArgc, gArgv); #ifndef XP_LINUX NS_SetCurrentThreadName("MainThread"); #endif AUTO_BASE_PROFILER_LABEL("XREMain::XRE_main (around Gecko Profiler)", OTHER); AUTO_PROFILER_INIT; AUTO_PROFILER_LABEL("XREMain::XRE_main", OTHER); #ifdef XP_MACOSX // We call this early because it will kick off a background-thread task // to register the fonts, and we'd like it to have a chance to complete // before gfxPlatform initialization actually requires it. gfxPlatformMac::RegisterSupplementalFonts(); #endif #ifdef MOZ_CODE_COVERAGE CodeCoverageHandler::Init(); #endif nsresult rv = NS_OK; if (aConfig.appData) { mAppData = MakeUnique(*aConfig.appData); } else { MOZ_RELEASE_ASSERT(aConfig.appDataPath); nsCOMPtr appini; rv = XRE_GetFileFromPath(aConfig.appDataPath, getter_AddRefs(appini)); if (NS_FAILED(rv)) { Output(true, "Error: unrecognized path: %s\n", aConfig.appDataPath); return 1; } mAppData = MakeUnique(); rv = XRE_ParseAppData(appini, *mAppData); if (NS_FAILED(rv)) { Output(true, "Couldn't read application.ini"); return 1; } appini->GetParent(getter_AddRefs(mAppData->directory)); } const char* appRemotingName = getenv("MOZ_APP_REMOTINGNAME"); if (appRemotingName) { mAppData->remotingName = appRemotingName; } else if (!mAppData->remotingName) { mAppData->remotingName = mAppData->name; } // used throughout this file gAppData = mAppData.get(); nsCOMPtr binFile; rv = XRE_GetBinaryPath(getter_AddRefs(binFile)); NS_ENSURE_SUCCESS(rv, 1); rv = binFile->GetPath(gAbsoluteArgv0Path); NS_ENSURE_SUCCESS(rv, 1); if (!mAppData->xreDirectory) { nsCOMPtr greDir; #if defined(MOZ_WIDGET_ANDROID) greDir = GreOmniPath(argc, argv); if (!greDir) { return 2; } #else rv = binFile->GetParent(getter_AddRefs(greDir)); if (NS_FAILED(rv)) return 2; #endif #ifdef XP_MACOSX nsCOMPtr parent; greDir->GetParent(getter_AddRefs(parent)); greDir = parent.forget(); greDir->AppendNative("Resources"_ns); #endif mAppData->xreDirectory = greDir; } #if defined(MOZ_WIDGET_ANDROID) nsCOMPtr dataDir; rv = binFile->GetParent(getter_AddRefs(dataDir)); if (NS_FAILED(rv)) return 2; mAppData->directory = dataDir; #else if (aConfig.appData && aConfig.appDataPath) { mAppData->xreDirectory->Clone(getter_AddRefs(mAppData->directory)); mAppData->directory->AppendNative(nsDependentCString(aConfig.appDataPath)); } #endif if (!mAppData->directory) { mAppData->directory = mAppData->xreDirectory; } #if defined(XP_WIN) # if defined(MOZ_SANDBOX) mAppData->sandboxBrokerServices = aConfig.sandboxBrokerServices; # endif // defined(MOZ_SANDBOX) { DebugOnly result = WindowsBCryptInitialization(); MOZ_ASSERT(result); } # if defined(_M_IX86) || defined(_M_X64) { DebugOnly result = WindowsMsctfInitialization(); MOZ_ASSERT(result); } # endif // _M_IX86 || _M_X64 #endif // defined(XP_WIN) // Once we unset the exception handler, we lose the ability to properly // detect hangs -- they show up as crashes. We do this as late as possible. // In particular, after ProcessRuntime is destroyed on Windows. auto unsetExceptionHandler = MakeScopeExit([&] { if (mAppData->flags & NS_XRE_ENABLE_CRASH_REPORTER) return CrashReporter::UnsetExceptionHandler(); return NS_OK; }); mozilla::AutoIOInterposer ioInterposerGuard; ioInterposerGuard.Init(); #if defined(XP_WIN) // We should have already done this when we created the skeleton UI. However, // there is code in here which needs xul in order to work, like EnsureMTA. It // should be setup that running it again is safe. mozilla::mscom::ProcessRuntime msCOMRuntime; #endif // init bool exit = false; int result = XRE_mainInit(&exit); if (result != 0 || exit) return result; // If we exit gracefully, remove the startup crash canary file. auto cleanup = MakeScopeExit([&]() -> nsresult { if (mProfLD) { nsCOMPtr crashFile; MOZ_TRY_VAR(crashFile, GetIncompleteStartupFile(mProfLD)); crashFile->Remove(false); } return NS_OK; }); // startup result = XRE_mainStartup(&exit); if (result != 0 || exit) return result; // Start the real application. We use |aInitJSContext = false| because // XRE_mainRun wants to initialize the JSContext after reading user prefs. mScopedXPCOM = MakeUnique(); rv = mScopedXPCOM->Initialize(/* aInitJSContext = */ false); NS_ENSURE_SUCCESS(rv, 1); // run! rv = XRE_mainRun(); #ifdef MOZ_X11 XRE_CleanupX11ErrorHandler(); #endif #ifdef MOZ_INSTRUMENT_EVENT_LOOP mozilla::ShutdownEventTracing(); #endif gAbsoluteArgv0Path.Truncate(); #if defined(MOZ_HAS_REMOTE) // Shut down the remote service. We must do this before calling LaunchChild // if we're restarting because otherwise the new instance will attempt to // remote to this instance. if (mRemoteService && !mDisableRemoteServer) { mRemoteService->ShutdownServer(); } #endif /* MOZ_WIDGET_GTK */ mScopedXPCOM = nullptr; // unlock the profile after ScopedXPCOMStartup object (xpcom) // has gone out of scope. see bug #386739 for more details mProfileLock->Unlock(); gProfileLock = nullptr; gLastAppVersion.Truncate(); gLastAppBuildID.Truncate(); #ifdef MOZ_WIDGET_GTK // gdk_display_close also calls gdk_display_manager_set_default_display // appropriately when necessary. if (!gfxPlatform::IsHeadless()) { # ifdef MOZ_WAYLAND WaylandDisplayRelease(); gWaylandProxy = nullptr; # endif } #endif mozilla::AppShutdown::MaybeDoRestart(); XRE_DeinitCommandLine(); if (NS_FAILED(rv)) { return 1; } return mozilla::AppShutdown::GetExitCode(); } void XRE_StopLateWriteChecks(void) { mozilla::StopLateWriteChecks(); } int XRE_main(int argc, char* argv[], const BootstrapConfig& aConfig) { XREMain main; int result = main.XRE_main(argc, argv, aConfig); mozilla::RecordShutdownEndTimeStamp(); #ifdef MOZ_BACKGROUNDTASKS // This is well after the profile has been unlocked, so it's okay if this does // delete this background task's temporary profile. mozilla::BackgroundTasks::Shutdown(); #endif return result; } nsresult XRE_InitCommandLine(int aArgc, char* aArgv[]) { nsresult rv = NS_OK; #if defined(XP_WIN) CommandLine::Init(aArgc, aArgv); #else // these leak on error, but that's OK: we'll just exit() char** canonArgs = new char*[aArgc]; // get the canonical version of the binary's path nsCOMPtr binFile; rv = XRE_GetBinaryPath(getter_AddRefs(binFile)); if (NS_FAILED(rv)) return NS_ERROR_FAILURE; nsAutoCString canonBinPath; rv = binFile->GetNativePath(canonBinPath); if (NS_FAILED(rv)) return NS_ERROR_FAILURE; canonArgs[0] = strdup(canonBinPath.get()); for (int i = 1; i < aArgc; ++i) { if (aArgv[i]) { canonArgs[i] = strdup(aArgv[i]); } } NS_ASSERTION(!CommandLine::IsInitialized(), "Bad news!"); CommandLine::Init(aArgc, canonArgs); for (int i = 0; i < aArgc; ++i) free(canonArgs[i]); delete[] canonArgs; #endif #if defined(MOZ_WIDGET_ANDROID) // gAppData is non-null iff this is the parent process. Otherwise, // the `-greomni`/`-appomni` flags are cross-platform and handled in // ContentProcess::Init. if (gAppData) { nsCOMPtr greOmni = gAppData->xreDirectory; if (!greOmni) { return NS_ERROR_FAILURE; } mozilla::Omnijar::Init(greOmni, greOmni); } #endif return rv; } nsresult XRE_DeinitCommandLine() { nsresult rv = NS_OK; CommandLine::Terminate(); return rv; } GeckoProcessType XRE_GetProcessType() { return GetGeckoProcessType(); } const char* XRE_GetProcessTypeString() { return XRE_GeckoProcessTypeToString(XRE_GetProcessType()); } bool XRE_IsE10sParentProcess() { #ifdef MOZ_WIDGET_ANDROID return XRE_IsParentProcess() && BrowserTabsRemoteAutostart() && mozilla::jni::IsAvailable(); #else return XRE_IsParentProcess() && BrowserTabsRemoteAutostart(); #endif } #define GECKO_PROCESS_TYPE(enum_value, enum_name, string_name, proc_typename, \ process_bin_type, procinfo_typename, \ webidl_typename, allcaps_name) \ bool XRE_Is##proc_typename##Process() { \ return XRE_GetProcessType() == GeckoProcessType_##enum_name; \ } #include "mozilla/GeckoProcessTypes.h" #undef GECKO_PROCESS_TYPE bool XRE_UseNativeEventProcessing() { switch (XRE_GetProcessType()) { #if defined(XP_MACOSX) || defined(XP_WIN) case GeckoProcessType_RDD: case GeckoProcessType_Socket: return false; case GeckoProcessType_Utility: { # if defined(XP_WIN) auto upc = mozilla::ipc::UtilityProcessChild::Get(); MOZ_ASSERT(upc); using SboxKind = mozilla::ipc::SandboxingKind; // These processes are used as external hosts for accessing Windows // APIs which (may) require a Windows native event loop. return upc->mSandbox == SboxKind::WINDOWS_UTILS || upc->mSandbox == SboxKind::WINDOWS_FILE_DIALOG; # else return false; # endif // defined(XP_WIN) } #endif // defined(XP_MACOSX) || defined(XP_WIN) case GeckoProcessType_GMPlugin: return mozilla::gmp::GMPProcessChild::UseNativeEventProcessing(); case GeckoProcessType_Content: return StaticPrefs::dom_ipc_useNativeEventProcessing_content(); default: return true; } } namespace mozilla { uint32_t GetMaxWebProcessCount() { // multiOptOut is in int to allow us to run multiple experiments without // introducing multiple prefs a la the autostart.N prefs. if (Preferences::GetInt("dom.ipc.multiOptOut", 0) >= nsIXULRuntime::E10S_MULTI_EXPERIMENT) { return 1; } const char* optInPref = "dom.ipc.processCount"; uint32_t optInPrefValue = Preferences::GetInt(optInPref, 1); return std::max(1u, optInPrefValue); } const char* PlatformBuildID() { return gToolkitBuildID; } } // namespace mozilla void SetupErrorHandling(const char* progname) { #ifdef XP_WIN /* On Windows XPSP3 and Windows Vista if DEP is configured off-by-default we still want DEP protection: enable it explicitly and programmatically. This function is not available on WinXPSP2 so we dynamically load it. */ HMODULE kernel32 = GetModuleHandleW(L"kernel32.dll"); SetProcessDEPPolicyFunc _SetProcessDEPPolicy = (SetProcessDEPPolicyFunc)GetProcAddress(kernel32, "SetProcessDEPPolicy"); if (_SetProcessDEPPolicy) _SetProcessDEPPolicy(PROCESS_DEP_ENABLE); #endif #ifdef XP_WIN // Suppress the "DLL Foo could not be found" dialog, such that if dependent // libraries (such as GDI+) are not preset, we gracefully fail to load those // XPCOM components, instead of being ungraceful. UINT realMode = SetErrorMode(0); realMode |= SEM_FAILCRITICALERRORS; // If XRE_NO_WINDOWS_CRASH_DIALOG is set, suppress displaying the "This // application has crashed" dialog box. This is mainly useful for // automated testing environments, e.g. tinderbox, where there's no need // for a dozen of the dialog boxes to litter the console if (getenv("XRE_NO_WINDOWS_CRASH_DIALOG")) realMode |= SEM_NOGPFAULTERRORBOX | SEM_NOOPENFILEERRORBOX; SetErrorMode(realMode); #endif InstallSignalHandlers(progname); // Unbuffer stdout, needed for tinderbox tests. setbuf(stdout, 0); } static bool gRunSelfAsContentProc = false; void XRE_EnableSameExecutableForContentProc() { if (!PR_GetEnv("MOZ_SEPARATE_CHILD_PROCESS")) { gRunSelfAsContentProc = true; } } mozilla::BinPathType XRE_GetChildProcBinPathType( GeckoProcessType aProcessType) { MOZ_ASSERT(aProcessType != GeckoProcessType_Default); if (!gRunSelfAsContentProc) { return BinPathType::PluginContainer; } #ifdef XP_WIN // On Windows, plugin-container may or may not be used depending on // the process type (e.g., actual plugins vs. content processes) switch (aProcessType) { # define GECKO_PROCESS_TYPE(enum_value, enum_name, string_name, \ proc_typename, process_bin_type, \ procinfo_typename, webidl_typename, allcaps_name) \ case GeckoProcessType_##enum_name: \ return BinPathType::process_bin_type; # include "mozilla/GeckoProcessTypes.h" # undef GECKO_PROCESS_TYPE default: return BinPathType::PluginContainer; } #else // On (non-macOS) Unix, plugin-container isn't used (except in cases // like xpcshell that are handled by the gRunSelfAsContentProc check // above). It isn't necessary the way it is on other platforms, and // it interferes with using the fork server. return BinPathType::Self; #endif } // From mozglue/static/rust/lib.rs extern "C" void install_rust_hooks(); struct InstallRustHooks { InstallRustHooks() { install_rust_hooks(); } }; InstallRustHooks sInstallRustHooks; #ifdef MOZ_ASAN_REPORTER void setASanReporterPath(nsIFile* aDir) { nsCOMPtr dir; aDir->Clone(getter_AddRefs(dir)); dir->Append(u"asan"_ns); nsresult rv = dir->Create(nsIFile::DIRECTORY_TYPE, 0700); if (NS_WARN_IF(NS_FAILED(rv) && rv != NS_ERROR_FILE_ALREADY_EXISTS)) { MOZ_CRASH("[ASan Reporter] Unable to create crash directory."); } dir->Append(u"ff_asan_log"_ns); # ifdef XP_WIN nsAutoString nspathW; rv = dir->GetPath(nspathW); NS_ConvertUTF16toUTF8 nspath(nspathW); # else nsAutoCString nspath; rv = dir->GetNativePath(nspath); # endif if (NS_FAILED(rv)) { MOZ_CRASH("[ASan Reporter] Unable to get native path for crash directory."); } __sanitizer_set_report_path(nspath.get()); } #endif