forked from mirrors/gecko-dev
		
	 075294349b
			
		
	
	
		075294349b
		
	
	
	
	
		
			
			The patch comes from https://gitlab.com/redhat/centos-stream/rpms/firefox/-/merge_requests/94 Firefox isn't currently handling window focusing right on X11... There's a few problems: If no startup id is provided, it tries to get user time from g_get_monotonic_time when there's no guarantee that timestamp is synchronized or even in a compatible unit. This merge request addressed that by dropping the code that uses g_get_monotonic_time entirely, and instead adding a heuristic to synthesize a startup id based on the time of the last user interaction on the display. This makes an assumption that firefox was started as a result of user interaction, which might not always hold true, but it's more likely than not, and is basically the same assumption firefox is already making by trying to use a timestamp of "now" (g_get_monotonic_time) when focusing the window. If a startup id is provided, it fails to tell gtk to use it when focusing the window because it calls gtk_window_present_with_time with some other random timestamp instead of passing GDK_CURRENT_TIME (which is the magic value that means "use startup id for timestamp"). This commit tries to detect when a startup id is available, and the display is X11, and if so use a timestamp of GDK_CURRENT_TIME. Differential Revision: https://phabricator.services.mozilla.com/D217174
		
			
				
	
	
		
			6306 lines
		
	
	
	
		
			196 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			6306 lines
		
	
	
	
		
			196 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| /* -*- 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 <sys/types.h>
 | |
| #  include <sys/sysctl.h>
 | |
| #endif
 | |
| 
 | |
| #include "prnetdb.h"
 | |
| #include "prprf.h"
 | |
| #include "prproces.h"
 | |
| #include "prenv.h"
 | |
| #include "prtime.h"
 | |
| 
 | |
| #include "nsIAppStartup.h"
 | |
| #include "nsCategoryManagerUtils.h"
 | |
| #include "nsIMutableArray.h"
 | |
| #include "nsCommandLine.h"
 | |
| #include "nsIComponentRegistrar.h"
 | |
| #include "nsIDialogParamBlock.h"
 | |
| #include "mozilla/ModuleUtils.h"
 | |
| #include "nsIIOService.h"
 | |
| #include "nsIObserverService.h"
 | |
| #include "nsINativeAppSupport.h"
 | |
| #include "nsIPlatformInfo.h"
 | |
| #include "nsIProcess.h"
 | |
| #include "nsIProfileUnlocker.h"
 | |
| #include "nsIPromptService.h"
 | |
| #include "nsIPropertyBag2.h"
 | |
| #include "nsIServiceManager.h"
 | |
| #include "nsIStringBundle.h"
 | |
| #include "nsISupportsPrimitives.h"
 | |
| #include "nsIToolkitProfile.h"
 | |
| #include "nsToolkitProfileService.h"
 | |
| #include "nsIURI.h"
 | |
| #include "nsIURL.h"
 | |
| #include "nsIWindowCreator.h"
 | |
| #include "nsIWindowWatcher.h"
 | |
| #include "nsIXULAppInfo.h"
 | |
| #include "nsIXULRuntime.h"
 | |
| #include "nsPIDOMWindow.h"
 | |
| #include "nsIWidget.h"
 | |
| #include "nsAppShellCID.h"
 | |
| #include "mozilla/dom/quota/QuotaManager.h"
 | |
| #include "mozilla/scache/StartupCache.h"
 | |
| #include "gfxPlatform.h"
 | |
| #include "PDMFactory.h"
 | |
| #ifdef XP_MACOSX
 | |
| #  include "gfxPlatformMac.h"
 | |
| #endif
 | |
| 
 | |
| #include "mozilla/Unused.h"
 | |
| 
 | |
| #ifdef XP_WIN
 | |
| #  include "nsIWinAppHelper.h"
 | |
| #  include <windows.h>
 | |
| #  include <intrin.h>
 | |
| #  include <math.h>
 | |
| #  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 <stdlib.h>
 | |
| #include <locale.h>
 | |
| 
 | |
| #ifdef XP_UNIX
 | |
| #  include <errno.h>
 | |
| #  include <pwd.h>
 | |
| #  include <string.h>
 | |
| #  include <sys/resource.h>
 | |
| #  include <sys/stat.h>
 | |
| #  include <unistd.h>
 | |
| #endif
 | |
| 
 | |
| #ifdef XP_WIN
 | |
| #  include <process.h>
 | |
| #  include <shlobj.h>
 | |
| #  include "mozilla/WinDllServices.h"
 | |
| #  include "nsThreadUtils.h"
 | |
| #  include "WinUtils.h"
 | |
| #endif
 | |
| 
 | |
| #ifdef XP_MACOSX
 | |
| #  include "nsILocalFileMac.h"
 | |
| #  include "nsCommandLineServiceMac.h"
 | |
| #endif
 | |
| 
 | |
| // for X remote support
 | |
| #if defined(MOZ_HAS_REMOTE)
 | |
| #  include "nsRemoteService.h"
 | |
| #endif
 | |
| 
 | |
| #if defined(DEBUG) && defined(XP_WIN)
 | |
| #  include <malloc.h>
 | |
| #endif
 | |
| 
 | |
| #if defined(XP_MACOSX)
 | |
| #  include <Carbon/Carbon.h>
 | |
| #endif
 | |
| 
 | |
| #ifdef DEBUG
 | |
| #  include "mozilla/Logging.h"
 | |
| #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
 | |
| #ifdef MOZ_ENABLE_DBUS
 | |
| #  include "DBusService.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 <glib.h>
 | |
| #  include "mozilla/WidgetUtilsGtk.h"
 | |
| #  include <gtk/gtk.h>
 | |
| #  ifdef MOZ_WAYLAND
 | |
| #    include <gdk/gdkwayland.h>
 | |
| #    include "mozilla/widget/nsWaylandDisplay.h"
 | |
| #    include "wayland-proxy.h"
 | |
| #  endif
 | |
| #  ifdef MOZ_X11
 | |
| #    include <gdk/gdkx.h>
 | |
| #  endif /* MOZ_X11 */
 | |
| #endif
 | |
| 
 | |
| #if defined(MOZ_WAYLAND)
 | |
| std::unique_ptr<WaylandProxy> 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 <data> will be stored in this pointer.
 | |
|  *        This is *not* allocated, but rather a pointer to the argv data.
 | |
|  * @param aFlags flags @see CheckArgFlag
 | |
|  */
 | |
| static ArgResult CheckArg(const char* aArg, const char** aParam = nullptr,
 | |
|                           CheckArgFlag aFlags = CheckArgFlag::RemoveArg) {
 | |
|   MOZ_ASSERT(gArgv, "gArgv must be initialized before CheckArg()");
 | |
|   return CheckArg(gArgc, gArgv, aArg, aParam, aFlags);
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Check for a commandline flag. Ignore data that's passed in with the flag.
 | |
|  * Flags may be in the form -arg or --arg (or /arg on win32).
 | |
|  * Will not remove flag if found.
 | |
|  *
 | |
|  * @param aArg the parameter to check. Must be lowercase.
 | |
|  */
 | |
| static ArgResult CheckArgExists(const char* aArg) {
 | |
|   return CheckArg(aArg, nullptr, CheckArgFlag::None);
 | |
| }
 | |
| 
 | |
| bool gSafeMode = false;
 | |
| bool gFxREmbedded = false;
 | |
| 
 | |
| enum E10sStatus {
 | |
|   kE10sEnabledByDefault,
 | |
|   kE10sForceDisabled,
 | |
| };
 | |
| 
 | |
| static bool gBrowserTabsRemoteAutostart = false;
 | |
| static E10sStatus gBrowserTabsRemoteStatus;
 | |
| static bool gBrowserTabsRemoteAutostartInitialized = false;
 | |
| 
 | |
| namespace mozilla {
 | |
| 
 | |
| bool BrowserTabsRemoteAutostart() {
 | |
|   if (gBrowserTabsRemoteAutostartInitialized) {
 | |
|     return gBrowserTabsRemoteAutostart;
 | |
|   }
 | |
|   gBrowserTabsRemoteAutostartInitialized = true;
 | |
| 
 | |
|   // If we're not in the parent process, we are running E10s.
 | |
|   if (!XRE_IsParentProcess()) {
 | |
|     gBrowserTabsRemoteAutostart = true;
 | |
|     return gBrowserTabsRemoteAutostart;
 | |
|   }
 | |
| 
 | |
|   gBrowserTabsRemoteAutostart = true;
 | |
|   E10sStatus status = kE10sEnabledByDefault;
 | |
| 
 | |
|   // We 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;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   // 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<nsIObserverService> observerService =
 | |
|           mozilla::services::GetObserverService()) {
 | |
|     nsCOMPtr<nsIObserver> shutdownObserver =
 | |
|         new Win32kEnrollmentStatusShutdownObserver();
 | |
|     observerService->AddObserver(shutdownObserver, "profile-before-change",
 | |
|                                  false);
 | |
|   }
 | |
| 
 | |
|   // If the user no longer qualifies because they edited a required pref, check
 | |
|   // that. [B] [E]
 | |
|   auto tmpStatus = GetLiveWin32kLockdownState();
 | |
|   if (Win32kExperimentEnrolled() && Win32kRequirementsUnsatisfied(tmpStatus)) {
 | |
|     Win32kExperimentDisqualify();
 | |
|     gWin32kExperimentStatus = nsIXULRuntime::eExperimentStatusDisqualified;
 | |
|   }
 | |
| 
 | |
|   // If the user has overridden an active experiment by setting a user value for
 | |
|   // "security.sandbox.content.win32k-disable", disqualify the user from the
 | |
|   // experiment. [A] [F]
 | |
|   if (Preferences::HasUserValue(kPrefWin32k) && Win32kExperimentEnrolled()) {
 | |
|     Win32kExperimentDisqualify();
 | |
|     gWin32kExperimentStatus = nsIXULRuntime::eExperimentStatusDisqualified;
 | |
|   }
 | |
| 
 | |
|   // Unlike Fission, we do not configure the default branch based on experiment
 | |
|   // enrollment status.  Instead we check the startupEnrollmentPref in
 | |
|   // GetContentWin32kLockdownState()
 | |
| 
 | |
|   Preferences::RegisterCallback(&OnWin32kChanged, kPrefWin32k);
 | |
| 
 | |
|   // Set the state
 | |
|   gWin32kStatus = GetLiveWin32kLockdownState();
 | |
| 
 | |
| #else
 | |
|   gWin32kStatus =
 | |
|       nsIXULRuntime::ContentWin32kLockdownState::OperatingSystemNotSupported;
 | |
|   gWin32kExperimentStatus = nsIXULRuntime::eExperimentStatusUnenrolled;
 | |
| 
 | |
| #endif  // XP_WIN
 | |
| }
 | |
| 
 | |
| nsIXULRuntime::ContentWin32kLockdownState GetWin32kLockdownState() {
 | |
| #ifdef XP_WIN
 | |
| 
 | |
|   mozilla::EnsureWin32kInitialized();
 | |
|   return gWin32kStatus;
 | |
| 
 | |
| #else
 | |
| 
 | |
|   return nsIXULRuntime::ContentWin32kLockdownState::OperatingSystemNotSupported;
 | |
| 
 | |
| #endif
 | |
| }
 | |
| 
 | |
| }  // namespace mozilla
 | |
| 
 | |
| // End Win32k Infrastructure ==========================================
 | |
| 
 | |
| // Fission Infrastructure =============================================
 | |
| 
 | |
| // Fission enablement for the current session is determined once, at startup,
 | |
| // and then remains the same for the duration of the session.
 | |
| //
 | |
| // The following factors determine whether or not Fission is enabled for a
 | |
| // session, in order of precedence:
 | |
| //
 | |
| // - Safe mode: In safe mode, Fission is never enabled.
 | |
| //
 | |
| // - The MOZ_FORCE_ENABLE_FISSION environment variable: If set to any value,
 | |
| //   Fission will be enabled.
 | |
| //
 | |
| // - The 'fission.autostart' preference, if it has been configured by the user.
 | |
| static const char kPrefFissionAutostart[] = "fission.autostart";
 | |
| 
 | |
| // The computed FissionAutostart value for the session, read by content
 | |
| // processes to initialize gFissionAutostart.
 | |
| //
 | |
| // This pref is locked, and only configured on the default branch, so should
 | |
| // never be persisted in a profile.
 | |
| static const char kPrefFissionAutostartSession[] = "fission.autostart.session";
 | |
| 
 | |
| //
 | |
| // The computed FissionAutostart value for the session, read by content
 | |
| // processes to initialize gFissionAutostart.
 | |
| 
 | |
| static bool gFissionAutostart = false;
 | |
| static bool gFissionAutostartInitialized = false;
 | |
| static nsIXULRuntime::FissionDecisionStatus gFissionDecisionStatus;
 | |
| static void EnsureFissionAutostartInitialized() {
 | |
|   if (gFissionAutostartInitialized) {
 | |
|     return;
 | |
|   }
 | |
|   gFissionAutostartInitialized = true;
 | |
| 
 | |
|   if (!XRE_IsParentProcess()) {
 | |
|     // This pref is configured for the current session by the parent process.
 | |
|     gFissionAutostart = Preferences::GetBool(kPrefFissionAutostartSession,
 | |
|                                              false, PrefValueKind::Default);
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   if (!BrowserTabsRemoteAutostart()) {
 | |
|     gFissionAutostart = false;
 | |
|     if (gBrowserTabsRemoteStatus == kE10sForceDisabled) {
 | |
|       gFissionDecisionStatus = nsIXULRuntime::eFissionDisabledByE10sEnv;
 | |
|     } else {
 | |
|       gFissionDecisionStatus = nsIXULRuntime::eFissionDisabledByE10sOther;
 | |
|     }
 | |
|   } else if (EnvHasValue("MOZ_FORCE_ENABLE_FISSION")) {
 | |
|     gFissionAutostart = true;
 | |
|     gFissionDecisionStatus = nsIXULRuntime::eFissionEnabledByEnv;
 | |
|   } else if (EnvHasValue("MOZ_FORCE_DISABLE_FISSION")) {
 | |
|     gFissionAutostart = false;
 | |
|     gFissionDecisionStatus = nsIXULRuntime::eFissionDisabledByEnv;
 | |
|   } else {
 | |
|     // NOTE: This will take into account changes to the default due to
 | |
|     // `InitializeFissionExperimentStatus`.
 | |
|     gFissionAutostart = Preferences::GetBool(kPrefFissionAutostart, false);
 | |
|     if (Preferences::HasUserValue(kPrefFissionAutostart)) {
 | |
|       gFissionDecisionStatus = gFissionAutostart
 | |
|                                    ? nsIXULRuntime::eFissionEnabledByUserPref
 | |
|                                    : nsIXULRuntime::eFissionDisabledByUserPref;
 | |
|     } else {
 | |
|       gFissionDecisionStatus = gFissionAutostart
 | |
|                                    ? nsIXULRuntime::eFissionEnabledByDefault
 | |
|                                    : nsIXULRuntime::eFissionDisabledByDefault;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   // Content processes cannot run the same logic as we're running in the parent
 | |
|   // process, as the current value of various preferences may have changed
 | |
|   // between launches. Instead, the content process will read the default branch
 | |
|   // of the locked `fission.autostart.session` preference to determine the value
 | |
|   // determined by this method.
 | |
|   Preferences::Unlock(kPrefFissionAutostartSession);
 | |
|   Preferences::ClearUser(kPrefFissionAutostartSession);
 | |
|   Preferences::SetBool(kPrefFissionAutostartSession, gFissionAutostart,
 | |
|                        PrefValueKind::Default);
 | |
|   Preferences::Lock(kPrefFissionAutostartSession);
 | |
| }
 | |
| 
 | |
| namespace mozilla {
 | |
| 
 | |
| bool FissionAutostart() {
 | |
|   EnsureFissionAutostartInitialized();
 | |
|   return gFissionAutostart;
 | |
| }
 | |
| 
 | |
| }  // namespace mozilla
 | |
| 
 | |
| // End Fission Infrastructure =========================================
 | |
| 
 | |
| namespace mozilla {
 | |
| 
 | |
| bool 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<int>(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<nsIFile> 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<ContentParent> unused =
 | |
|       ContentParent::GetNewOrUsedBrowserProcess(DEFAULT_REMOTE_TYPE);
 | |
|   return NS_OK;
 | |
| }
 | |
| 
 | |
| NS_IMETHODIMP
 | |
| nsXULAppInfo::InvalidateCachesOnRestart() {
 | |
|   nsCOMPtr<nsIFile> file;
 | |
|   nsresult rv =
 | |
|       NS_GetSpecialDirectory(NS_APP_PROFILE_DIR_STARTUP, getter_AddRefs(file));
 | |
|   if (NS_FAILED(rv)) return rv;
 | |
|   if (!file) return NS_ERROR_NOT_AVAILABLE;
 | |
| 
 | |
|   file->AppendNative(FILE_COMPATIBILITY_INFO);
 | |
| 
 | |
|   nsINIParser parser;
 | |
|   rv = parser.Init(file);
 | |
|   if (NS_FAILED(rv)) {
 | |
|     // This fails if compatibility.ini is not there, so we'll
 | |
|     // flush the caches on the next restart anyways.
 | |
|     return NS_OK;
 | |
|   }
 | |
| 
 | |
|   nsAutoCString buf;
 | |
|   rv = parser.GetString("Compatibility", "InvalidateCaches", buf);
 | |
| 
 | |
|   if (NS_FAILED(rv)) {
 | |
|     PRFileDesc* fd;
 | |
|     rv = file->OpenNSPRFileDesc(PR_RDWR | PR_APPEND, 0600, &fd);
 | |
|     if (NS_FAILED(rv)) {
 | |
|       NS_ERROR("could not create output stream");
 | |
|       return NS_ERROR_NOT_AVAILABLE;
 | |
|     }
 | |
|     static const char kInvalidationHeader[] =
 | |
|         NS_LINEBREAK "InvalidateCaches=1" NS_LINEBREAK;
 | |
|     PR_Write(fd, kInvalidationHeader, sizeof(kInvalidationHeader) - 1);
 | |
|     PR_Close(fd);
 | |
|   }
 | |
|   return NS_OK;
 | |
| }
 | |
| 
 | |
| 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<LauncherRegistryInfo::EnabledState> gLauncherProcessState;
 | |
| #endif  // defined(XP_WIN) && defined(MOZ_LAUNCHER_PROCESS)
 | |
| 
 | |
| NS_IMETHODIMP
 | |
| nsXULAppInfo::GetLauncherProcessState(uint32_t* aResult) {
 | |
| #if defined(XP_WIN) && defined(MOZ_LAUNCHER_PROCESS)
 | |
|   SetupLauncherProcessPref();
 | |
| 
 | |
|   if (!gLauncherProcessState) {
 | |
|     return NS_ERROR_UNEXPECTED;
 | |
|   }
 | |
| 
 | |
|   *aResult = static_cast<uint32_t>(gLauncherProcessState.value());
 | |
|   return NS_OK;
 | |
| #else
 | |
|   return NS_ERROR_NOT_AVAILABLE;
 | |
| #endif
 | |
| }
 | |
| 
 | |
| #ifdef XP_WIN
 | |
| NS_IMETHODIMP
 | |
| nsXULAppInfo::GetUserCanElevate(bool* aUserCanElevate) {
 | |
|   HANDLE rawToken;
 | |
|   if (!::OpenProcessToken(::GetCurrentProcess(), TOKEN_QUERY, &rawToken)) {
 | |
|     *aUserCanElevate = false;
 | |
|     return NS_OK;
 | |
|   }
 | |
| 
 | |
|   nsAutoHandle token(rawToken);
 | |
|   LauncherResult<TOKEN_ELEVATION_TYPE> elevationType = GetElevationType(token);
 | |
|   if (elevationType.isErr()) {
 | |
|     *aUserCanElevate = false;
 | |
|     return NS_OK;
 | |
|   }
 | |
| 
 | |
|   // The possible values returned for elevationType and their meanings are:
 | |
|   //   TokenElevationTypeDefault: The token does not have a linked token
 | |
|   //     (e.g. UAC disabled or a standard user, so they can't be elevated)
 | |
|   //   TokenElevationTypeFull: The token is linked to an elevated token
 | |
|   //     (e.g. UAC is enabled and the user is already elevated so they can't
 | |
|   //      be elevated again)
 | |
|   //   TokenElevationTypeLimited: The token is linked to a limited token
 | |
|   //     (e.g. UAC is enabled and the user is not elevated, so they can be
 | |
|   //      elevated)
 | |
|   *aUserCanElevate = (elevationType.inspect() == TokenElevationTypeLimited);
 | |
|   return NS_OK;
 | |
| }
 | |
| #endif
 | |
| 
 | |
| NS_IMETHODIMP
 | |
| nsXULAppInfo::GetCrashReporterEnabled(bool* aEnabled) {
 | |
|   *aEnabled = CrashReporter::GetEnabled();
 | |
|   return NS_OK;
 | |
| }
 | |
| 
 | |
| NS_IMETHODIMP
 | |
| nsXULAppInfo::SetEnabled(bool aEnabled) {
 | |
|   if (aEnabled) {
 | |
|     if (CrashReporter::GetEnabled()) {
 | |
|       // no point in erroring for double-enabling
 | |
|       return NS_OK;
 | |
|     }
 | |
| 
 | |
|     nsCOMPtr<nsIFile> greBinDir;
 | |
|     NS_GetSpecialDirectory(NS_GRE_BIN_DIR, getter_AddRefs(greBinDir));
 | |
|     if (!greBinDir) {
 | |
|       return NS_ERROR_FAILURE;
 | |
|     }
 | |
| 
 | |
|     nsCOMPtr<nsIFile> xreBinDirectory = greBinDir;
 | |
|     if (!xreBinDirectory) {
 | |
|       return NS_ERROR_FAILURE;
 | |
|     }
 | |
| 
 | |
|     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<nsIURI> uri;
 | |
|   NS_NewURI(getter_AddRefs(uri), data);
 | |
|   if (!uri) return NS_ERROR_FAILURE;
 | |
| 
 | |
|   nsCOMPtr<nsIURL> url;
 | |
|   url = do_QueryInterface(uri);
 | |
|   NS_ADDREF(*aServerURL = url);
 | |
| 
 | |
|   return NS_OK;
 | |
| }
 | |
| 
 | |
| NS_IMETHODIMP
 | |
| nsXULAppInfo::SetServerURL(nsIURL* aServerURL) {
 | |
|   // Only allow https or http URLs
 | |
|   if (!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<EXCEPTION_POINTERS*>(aExceptionInfo));
 | |
| #else
 | |
|   return NS_ERROR_NOT_IMPLEMENTED;
 | |
| #endif
 | |
| }
 | |
| 
 | |
| NS_IMETHODIMP
 | |
| nsXULAppInfo::AppendObjCExceptionInfoToAppNotes(void* aException) {
 | |
| #ifdef XP_MACOSX
 | |
|   return CrashReporter::AppendObjCExceptionInfoToAppNotes(aException);
 | |
| #else
 | |
|   return NS_ERROR_NOT_IMPLEMENTED;
 | |
| #endif
 | |
| }
 | |
| 
 | |
| NS_IMETHODIMP
 | |
| nsXULAppInfo::GetSubmitReports(bool* aEnabled) {
 | |
|   return CrashReporter::GetSubmitReports(aEnabled);
 | |
| }
 | |
| 
 | |
| NS_IMETHODIMP
 | |
| nsXULAppInfo::SetSubmitReports(bool aEnabled) {
 | |
|   return CrashReporter::SetSubmitReports(aEnabled);
 | |
| }
 | |
| 
 | |
| NS_IMETHODIMP
 | |
| nsXULAppInfo::UpdateCrashEventsDir() {
 | |
|   CrashReporter::UpdateCrashEventsDir();
 | |
|   return NS_OK;
 | |
| }
 | |
| 
 | |
| NS_IMETHODIMP
 | |
| nsXULAppInfo::SaveMemoryReport() {
 | |
|   if (!CrashReporter::GetEnabled()) {
 | |
|     return NS_ERROR_NOT_INITIALIZED;
 | |
|   }
 | |
|   nsCOMPtr<nsIFile> file;
 | |
|   nsresult rv = CrashReporter::GetDefaultMemoryReportFile(getter_AddRefs(file));
 | |
|   if (NS_WARN_IF(NS_FAILED(rv))) {
 | |
|     return rv;
 | |
|   }
 | |
| 
 | |
|   nsString path;
 | |
|   file->GetPath(path);
 | |
| 
 | |
|   nsCOMPtr<nsIMemoryInfoDumper> dumper =
 | |
|       do_GetService("@mozilla.org/memory-info-dumper;1");
 | |
|   if (NS_WARN_IF(!dumper)) {
 | |
|     return NS_ERROR_UNEXPECTED;
 | |
|   }
 | |
| 
 | |
|   rv = dumper->DumpMemoryReportsToNamedFile(
 | |
|       path, this, file, true /* anonymize */, false /* minimizeMemoryUsage */);
 | |
|   if (NS_WARN_IF(NS_FAILED(rv))) {
 | |
|     return rv;
 | |
|   }
 | |
|   return NS_OK;
 | |
| }
 | |
| 
 | |
| // This method is from nsIFInishDumpingCallback.
 | |
| NS_IMETHODIMP
 | |
| nsXULAppInfo::Callback(nsISupports* aData) {
 | |
|   nsCOMPtr<nsIFile> file = do_QueryInterface(aData);
 | |
|   MOZ_ASSERT(file);
 | |
| 
 | |
|   CrashReporter::SetMemoryReportFile(file);
 | |
|   return NS_OK;
 | |
| }
 | |
| 
 | |
| static const nsXULAppInfo kAppInfo;
 | |
| namespace mozilla {
 | |
| nsresult AppInfoConstructor(REFNSIID aIID, void** aResult) {
 | |
|   return const_cast<nsXULAppInfo*>(&kAppInfo)->QueryInterface(aIID, aResult);
 | |
| }
 | |
| }  // namespace mozilla
 | |
| 
 | |
| bool gLogConsoleErrors = false;
 | |
| 
 | |
| #define NS_ENSURE_TRUE_LOG(x, ret)               \
 | |
|   PR_BEGIN_MACRO                                 \
 | |
|   if (MOZ_UNLIKELY(!(x))) {                      \
 | |
|     NS_WARNING("NS_ENSURE_TRUE(" #x ") failed"); \
 | |
|     gLogConsoleErrors = true;                    \
 | |
|     return ret;                                  \
 | |
|   }                                              \
 | |
|   PR_END_MACRO
 | |
| 
 | |
| #define NS_ENSURE_SUCCESS_LOG(res, ret) \
 | |
|   NS_ENSURE_TRUE_LOG(NS_SUCCEEDED(res), ret)
 | |
| 
 | |
| /**
 | |
|  * Because we're starting/stopping XPCOM several times in different scenarios,
 | |
|  * this class is a stack-based critter that makes sure that XPCOM is shut down
 | |
|  * during early returns.
 | |
|  */
 | |
| 
 | |
| class ScopedXPCOMStartup {
 | |
|  public:
 | |
|   ScopedXPCOMStartup() : mServiceManager(nullptr) {}
 | |
|   ~ScopedXPCOMStartup();
 | |
| 
 | |
|   nsresult Initialize(bool aInitJSContext = true);
 | |
|   nsresult SetWindowCreator(nsINativeAppSupport* native);
 | |
| 
 | |
|  private:
 | |
|   nsIServiceManager* mServiceManager;
 | |
|   static nsINativeAppSupport* gNativeAppSupport;
 | |
| 
 | |
|   friend already_AddRefed<nsINativeAppSupport> NS_GetNativeAppSupport();
 | |
| };
 | |
| 
 | |
| ScopedXPCOMStartup::~ScopedXPCOMStartup() {
 | |
|   NS_IF_RELEASE(gNativeAppSupport);
 | |
| 
 | |
|   if (mServiceManager) {
 | |
| #ifdef XP_MACOSX
 | |
|     // On OS X, we need a pool to catch cocoa objects that are autoreleased
 | |
|     // during teardown.
 | |
|     mozilla::MacAutoreleasePool pool;
 | |
| #endif
 | |
| 
 | |
|     nsCOMPtr<nsIAppStartup> appStartup(components::AppStartup::Service());
 | |
|     if (appStartup) appStartup->DestroyHiddenWindow();
 | |
| 
 | |
|     gDirServiceProvider->DoShutdown();
 | |
|     PROFILER_MARKER_UNTYPED("Shutdown early", OTHER);
 | |
| 
 | |
|     WriteConsoleLog();
 | |
| 
 | |
|     NS_ShutdownXPCOM(mServiceManager);
 | |
|     mServiceManager = nullptr;
 | |
|   }
 | |
| }
 | |
| 
 | |
| nsresult ScopedXPCOMStartup::Initialize(bool aInitJSContext) {
 | |
|   NS_ASSERTION(gDirServiceProvider, "Should not get here!");
 | |
| 
 | |
|   nsresult rv;
 | |
| 
 | |
|   rv = NS_InitXPCOM(&mServiceManager, gDirServiceProvider->GetAppDir(),
 | |
|                     gDirServiceProvider, aInitJSContext);
 | |
|   if (NS_FAILED(rv)) {
 | |
|     NS_ERROR("Couldn't start xpcom!");
 | |
|     mServiceManager = nullptr;
 | |
|   } else {
 | |
| #ifdef DEBUG
 | |
|     nsCOMPtr<nsIComponentRegistrar> reg = do_QueryInterface(mServiceManager);
 | |
|     NS_ASSERTION(reg, "Service Manager doesn't QI to Registrar.");
 | |
| #endif
 | |
|   }
 | |
| 
 | |
|   return rv;
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * This is a little factory class that serves as a singleton-service-factory
 | |
|  * for the nativeappsupport object.
 | |
|  */
 | |
| class nsSingletonFactory final : public nsIFactory {
 | |
|  public:
 | |
|   NS_DECL_ISUPPORTS
 | |
|   NS_DECL_NSIFACTORY
 | |
| 
 | |
|   explicit nsSingletonFactory(nsISupports* aSingleton);
 | |
| 
 | |
|  private:
 | |
|   ~nsSingletonFactory() = default;
 | |
|   nsCOMPtr<nsISupports> mSingleton;
 | |
| };
 | |
| 
 | |
| nsSingletonFactory::nsSingletonFactory(nsISupports* aSingleton)
 | |
|     : mSingleton(aSingleton) {
 | |
|   NS_ASSERTION(mSingleton, "Singleton was null!");
 | |
| }
 | |
| 
 | |
| NS_IMPL_ISUPPORTS(nsSingletonFactory, nsIFactory)
 | |
| 
 | |
| NS_IMETHODIMP
 | |
| nsSingletonFactory::CreateInstance(const nsIID& aIID, void** aResult) {
 | |
|   return mSingleton->QueryInterface(aIID, aResult);
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Set our windowcreator on the WindowWatcher service.
 | |
|  */
 | |
| nsresult ScopedXPCOMStartup::SetWindowCreator(nsINativeAppSupport* native) {
 | |
|   nsresult rv;
 | |
| 
 | |
|   NS_IF_ADDREF(gNativeAppSupport = native);
 | |
| 
 | |
|   nsCOMPtr<nsIWindowCreator> creator(components::AppStartup::Service());
 | |
|   if (!creator) return NS_ERROR_UNEXPECTED;
 | |
| 
 | |
|   nsCOMPtr<nsIWindowWatcher> wwatch(
 | |
|       do_GetService(NS_WINDOWWATCHER_CONTRACTID, &rv));
 | |
|   NS_ENSURE_SUCCESS(rv, rv);
 | |
| 
 | |
|   return wwatch->SetWindowCreator(creator);
 | |
| }
 | |
| 
 | |
| /* static */ already_AddRefed<nsINativeAppSupport> NS_GetNativeAppSupport() {
 | |
|   if (!ScopedXPCOMStartup::gNativeAppSupport) {
 | |
|     return nullptr;
 | |
|   }
 | |
| 
 | |
|   return do_AddRef(ScopedXPCOMStartup::gNativeAppSupport);
 | |
| }
 | |
| 
 | |
| nsINativeAppSupport* ScopedXPCOMStartup::gNativeAppSupport;
 | |
| 
 | |
| static void DumpArbitraryHelp() {
 | |
|   nsresult rv;
 | |
| 
 | |
|   ScopedLogging log;
 | |
| 
 | |
|   {
 | |
|     ScopedXPCOMStartup xpcom;
 | |
|     xpcom.Initialize();
 | |
| 
 | |
|     nsCOMPtr<nsICommandLineRunner> cmdline(new nsCommandLine());
 | |
| 
 | |
|     nsCString text;
 | |
|     rv = cmdline->GetHelpText(text);
 | |
|     if (NS_SUCCEEDED(rv)) printf("%s", text.get());
 | |
|   }
 | |
| }
 | |
| 
 | |
| // English text needs to go into a dtd file.
 | |
| // But when this is called we have no components etc. These strings must either
 | |
| // be here, or in a native resource file.
 | |
| static void DumpHelp() {
 | |
|   printf(
 | |
|       "Usage: %s [ options ... ] [URL]\n"
 | |
|       "       where options include:\n\n",
 | |
|       gArgv[0]);
 | |
| 
 | |
| #ifdef MOZ_X11
 | |
|   printf(
 | |
|       "X11 options\n"
 | |
|       "  --display=DISPLAY  X display to use\n"
 | |
|       "  --sync             Make X calls synchronous\n");
 | |
| #endif
 | |
| #ifdef XP_UNIX
 | |
|   printf(
 | |
|       "  --g-fatal-warnings Make all warnings fatal\n"
 | |
|       "\n%s options\n",
 | |
|       (const char*)gAppData->name);
 | |
| #endif
 | |
| 
 | |
|   printf(
 | |
|       "  -h or --help       Print this message.\n"
 | |
|       "  -v or --version    Print %s version.\n"
 | |
|       "  --full-version     Print %s version, build and platform build ids.\n"
 | |
|       "  -P <profile>       Start with <profile>.\n"
 | |
|       "  --profile <path>   Start with profile at <path>.\n"
 | |
|       "  --migration        Start with migration wizard.\n"
 | |
|       "  --ProfileManager   Start with ProfileManager.\n"
 | |
| #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=<modules> Treated as MOZ_LOG=<modules> environment "
 | |
|       "variable,\n"
 | |
|       "                     overrides it.\n"
 | |
|       "  --MOZ_LOG_FILE=<file> Treated as MOZ_LOG_FILE=<file> environment "
 | |
|       "variable,\n"
 | |
|       "                     overrides it. If MOZ_LOG_FILE is not specified as "
 | |
|       "an\n"
 | |
|       "                     argument or as an environment variable, logging "
 | |
|       "will be\n"
 | |
|       "                     written to stdout.\n",
 | |
|       (const char*)gAppData->name, (const char*)gAppData->name);
 | |
| 
 | |
| #if defined(XP_WIN)
 | |
|   printf("  --console          Start %s with a debugging console.\n",
 | |
|          (const char*)gAppData->name);
 | |
| #endif
 | |
| 
 | |
| #if defined(XP_WIN) || defined(MOZ_WIDGET_GTK) || defined(XP_MACOSX)
 | |
|   printf("  --headless         Run without a GUI.\n");
 | |
| #endif
 | |
| 
 | |
| #if defined(MOZ_ENABLE_DBUS)
 | |
|   printf(
 | |
|       "  --dbus-service <launcher>  Run as DBus service for "
 | |
|       "org.freedesktop.Application and\n"
 | |
|       "                             set a launcher (usually /usr/bin/appname "
 | |
|       "script) for it.");
 | |
| #endif
 | |
| 
 | |
|   // this works, but only after the components have registered.  so if you drop
 | |
|   // in a new command line handler, --help won't not until the second run. out
 | |
|   // of the bug, because we ship a component.reg file, it works correctly.
 | |
|   DumpArbitraryHelp();
 | |
| }
 | |
| 
 | |
| static inline void DumpVersion() {
 | |
|   if (gAppData->vendor && *gAppData->vendor) {
 | |
|     printf("%s ", (const char*)gAppData->vendor);
 | |
|   }
 | |
|   printf("%s ", (const char*)gAppData->name);
 | |
| 
 | |
|   // Use the displayed version
 | |
|   // For example, for beta, we would display 42.0b2 instead of 42.0
 | |
|   printf("%s", MOZ_STRINGIFY(MOZ_APP_VERSION_DISPLAY));
 | |
| 
 | |
|   if (gAppData->copyright && *gAppData->copyright) {
 | |
|     printf(", %s", (const char*)gAppData->copyright);
 | |
|   }
 | |
|   printf("\n");
 | |
| }
 | |
| 
 | |
| static inline void DumpFullVersion() {
 | |
|   if (gAppData->vendor && *gAppData->vendor) {
 | |
|     printf("%s ", (const char*)gAppData->vendor);
 | |
|   }
 | |
|   printf("%s ", (const char*)gAppData->name);
 | |
| 
 | |
|   // Use the displayed version
 | |
|   // For example, for beta, we would display 42.0b2 instead of 42.0
 | |
|   printf("%s ", MOZ_STRINGIFY(MOZ_APP_VERSION_DISPLAY));
 | |
| 
 | |
|   printf("%s ", (const char*)gAppData->buildID);
 | |
|   printf("%s ", (const char*)PlatformBuildID());
 | |
|   if (gAppData->copyright && *gAppData->copyright) {
 | |
|     printf(", %s", (const char*)gAppData->copyright);
 | |
|   }
 | |
|   printf("\n");
 | |
| }
 | |
| 
 | |
| void XRE_InitOmnijar(nsIFile* greOmni, nsIFile* appOmni) {
 | |
|   mozilla::Omnijar::Init(greOmni, appOmni);
 | |
| }
 | |
| 
 | |
| nsresult XRE_GetBinaryPath(nsIFile** aResult) {
 | |
|   return mozilla::BinaryPath::GetFile(aResult);
 | |
| }
 | |
| 
 | |
| #ifdef XP_WIN
 | |
| #  include "nsWindowsRestart.cpp"
 | |
| #  include <shellapi.h>
 | |
| 
 | |
| typedef BOOL(WINAPI* SetProcessDEPPolicyFunc)(DWORD dwFlags);
 | |
| 
 | |
| static void RegisterApplicationRestartChanged(const char* aPref, void* aData) {
 | |
|   DWORD cchCmdLine = 0;
 | |
|   HRESULT rc = ::GetApplicationRestartSettings(::GetCurrentProcess(), nullptr,
 | |
|                                                &cchCmdLine, nullptr);
 | |
|   bool wasRegistered = false;
 | |
|   if (rc == S_OK) {
 | |
|     wasRegistered = true;
 | |
|   }
 | |
| 
 | |
|   if (Preferences::GetBool(PREF_WIN_REGISTER_APPLICATION_RESTART, false) &&
 | |
|       !wasRegistered) {
 | |
|     // Make the command line to use when restarting.
 | |
|     // Excludes argv[0] because RegisterApplicationRestart adds the
 | |
|     // executable name, replace that temporarily with -os-restarted
 | |
|     char* exeName = gRestartArgv[0];
 | |
|     gRestartArgv[0] = const_cast<char*>("-os-restarted");
 | |
|     wchar_t** restartArgvConverted =
 | |
|         AllocConvertUTF8toUTF16Strings(gRestartArgc, gRestartArgv);
 | |
|     gRestartArgv[0] = exeName;
 | |
| 
 | |
|     mozilla::UniquePtr<wchar_t[]> restartCommandLine;
 | |
|     if (restartArgvConverted) {
 | |
|       restartCommandLine =
 | |
|           mozilla::MakeCommandLine(gRestartArgc, restartArgvConverted);
 | |
|       FreeAllocStrings(gRestartArgc, restartArgvConverted);
 | |
|     }
 | |
| 
 | |
|     if (restartCommandLine) {
 | |
|       // Flags RESTART_NO_PATCH and RESTART_NO_REBOOT are not set, so we
 | |
|       // should be restarted if terminated by an update or restart.
 | |
|       ::RegisterApplicationRestart(restartCommandLine.get(),
 | |
|                                    RESTART_NO_CRASH | RESTART_NO_HANG);
 | |
|     }
 | |
|   } else if (wasRegistered) {
 | |
|     ::UnregisterApplicationRestart();
 | |
|   }
 | |
| }
 | |
| 
 | |
| static void OnAlteredPrefetchPrefChanged(const char* aPref, void* aData) {
 | |
|   int32_t prefVal = Preferences::GetInt(PREF_WIN_ALTERED_DLL_PREFETCH, 0);
 | |
| 
 | |
|   mozilla::DllPrefetchExperimentRegistryInfo prefetchRegInfo;
 | |
|   mozilla::DebugOnly<mozilla::Result<Ok, nsresult>> reflectResult =
 | |
|       prefetchRegInfo.ReflectPrefToRegistry(prefVal);
 | |
| 
 | |
|   MOZ_ASSERT(reflectResult.value.isOk());
 | |
| }
 | |
| 
 | |
| static void SetupAlteredPrefetchPref() {
 | |
|   mozilla::DllPrefetchExperimentRegistryInfo prefetchRegInfo;
 | |
| 
 | |
|   mozilla::DebugOnly<mozilla::Result<Ok, nsresult>> reflectResult =
 | |
|       prefetchRegInfo.ReflectPrefToRegistry(
 | |
|           Preferences::GetInt(PREF_WIN_ALTERED_DLL_PREFETCH, 0));
 | |
|   MOZ_ASSERT(reflectResult.value.isOk());
 | |
| 
 | |
|   Preferences::RegisterCallback(&OnAlteredPrefetchPrefChanged,
 | |
|                                 PREF_WIN_ALTERED_DLL_PREFETCH);
 | |
| }
 | |
| 
 | |
| static 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<mozilla::LauncherVoidResult> reflectResult =
 | |
|       launcherRegInfo.ReflectPrefToRegistry(prefVal);
 | |
|   MOZ_ASSERT(reflectResult.inspect().isOk());
 | |
| }
 | |
| 
 | |
| static void OnLauncherTelemetryPrefChanged(const char* aPref, void* aData) {
 | |
|   bool prefVal = Preferences::GetBool(kPrefHealthReportUploadEnabled, true);
 | |
| 
 | |
|   mozilla::LauncherRegistryInfo launcherRegInfo;
 | |
|   mozilla::DebugOnly<mozilla::LauncherVoidResult> reflectResult =
 | |
|       launcherRegInfo.ReflectTelemetryPrefToRegistry(prefVal);
 | |
|   MOZ_ASSERT(reflectResult.inspect().isOk());
 | |
| }
 | |
| 
 | |
| static void SetupLauncherProcessPref() {
 | |
|   if (gLauncherProcessState) {
 | |
|     // We've already successfully run
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   mozilla::LauncherRegistryInfo launcherRegInfo;
 | |
| 
 | |
|   mozilla::LauncherResult<mozilla::LauncherRegistryInfo::EnabledState>
 | |
|       enabledState = launcherRegInfo.IsEnabled();
 | |
| 
 | |
|   if (enabledState.isOk()) {
 | |
|     gLauncherProcessState = Some(enabledState.unwrap());
 | |
| 
 | |
|     CrashReporter::RecordAnnotationU32(
 | |
|         CrashReporter::Annotation::LauncherProcessState,
 | |
|         static_cast<uint32_t>(enabledState.unwrap()));
 | |
| 
 | |
|     // Reflect the launcher process registry state into user prefs
 | |
|     Preferences::SetBool(
 | |
|         PREF_WIN_LAUNCHER_PROCESS_ENABLED,
 | |
|         enabledState.unwrap() !=
 | |
|             mozilla::LauncherRegistryInfo::EnabledState::ForceDisabled);
 | |
|   }
 | |
| 
 | |
|   mozilla::DebugOnly<mozilla::LauncherVoidResult> reflectResult =
 | |
|       launcherRegInfo.ReflectTelemetryPrefToRegistry(
 | |
|           Preferences::GetBool(kPrefHealthReportUploadEnabled, true));
 | |
|   MOZ_ASSERT(reflectResult.inspect().isOk());
 | |
| 
 | |
|   Preferences::RegisterCallback(&OnLauncherPrefChanged,
 | |
|                                 PREF_WIN_LAUNCHER_PROCESS_ENABLED);
 | |
|   Preferences::RegisterCallback(&OnLauncherTelemetryPrefChanged,
 | |
|                                 kPrefHealthReportUploadEnabled);
 | |
| }
 | |
| 
 | |
| #  endif  // defined(MOZ_LAUNCHER_PROCESS)
 | |
| 
 | |
| #  if defined(MOZ_DEFAULT_BROWSER_AGENT)
 | |
| 
 | |
| #    define DEFAULT_BROWSER_AGENT_KEY_NAME \
 | |
|       "SOFTWARE\\" MOZ_APP_VENDOR "\\" MOZ_APP_NAME "\\Default Browser Agent"
 | |
| 
 | |
| static nsresult PrependRegistryValueName(nsAutoString& aValueName) {
 | |
|   nsresult rv;
 | |
| 
 | |
|   nsCOMPtr<nsIFile> binaryPath;
 | |
|   rv = XRE_GetBinaryPath(getter_AddRefs(binaryPath));
 | |
|   NS_ENSURE_SUCCESS(rv, rv);
 | |
| 
 | |
|   nsCOMPtr<nsIFile> binaryDir;
 | |
|   rv = binaryPath->GetParent(getter_AddRefs(binaryDir));
 | |
|   NS_ENSURE_SUCCESS(rv, rv);
 | |
| 
 | |
|   nsAutoString prefix;
 | |
|   rv = binaryDir->GetPath(prefix);
 | |
|   NS_ENSURE_SUCCESS(rv, rv);
 | |
| 
 | |
|   prefix.AppendLiteral("|");
 | |
|   aValueName.Insert(prefix, 0);
 | |
| 
 | |
|   return NS_OK;
 | |
| }
 | |
| 
 | |
| static void OnDefaultAgentTelemetryPrefChanged(const char* aPref, void* aData) {
 | |
|   nsresult rv;
 | |
|   nsAutoString valueName;
 | |
|   if (strcmp(aPref, kPrefHealthReportUploadEnabled) == 0) {
 | |
|     valueName.AssignLiteral("DisableTelemetry");
 | |
|   } else if (strcmp(aPref, kPrefDefaultAgentEnabled) == 0) {
 | |
|     valueName.AssignLiteral("DisableDefaultBrowserAgent");
 | |
|   } else {
 | |
|     return;
 | |
|   }
 | |
|   rv = PrependRegistryValueName(valueName);
 | |
|   NS_ENSURE_SUCCESS_VOID(rv);
 | |
| 
 | |
|   nsCOMPtr<nsIWindowsRegKey> regKey =
 | |
|       do_CreateInstance("@mozilla.org/windows-registry-key;1", &rv);
 | |
|   NS_ENSURE_SUCCESS_VOID(rv);
 | |
| 
 | |
|   nsAutoString keyName;
 | |
|   keyName.AppendLiteral(DEFAULT_BROWSER_AGENT_KEY_NAME);
 | |
|   rv = regKey->Create(nsIWindowsRegKey::ROOT_KEY_CURRENT_USER, keyName,
 | |
|                       nsIWindowsRegKey::ACCESS_WRITE);
 | |
| 
 | |
|   bool prefVal = Preferences::GetBool(aPref, true);
 | |
| 
 | |
|   // We're recording whether the pref is *disabled*, so invert the value.
 | |
|   rv = regKey->WriteIntValue(valueName, prefVal ? 0 : 1);
 | |
|   NS_ENSURE_SUCCESS_VOID(rv);
 | |
| }
 | |
| 
 | |
| static void OnSetDefaultBrowserUserChoicePrefChanged(const char* aPref,
 | |
|                                                      void* aData) {
 | |
|   nsresult rv;
 | |
|   if (strcmp(aPref, kPrefSetDefaultBrowserUserChoicePref) != 0) {
 | |
|     return;
 | |
|   }
 | |
|   nsAutoString valueName;
 | |
|   valueName.AssignLiteral("SetDefaultBrowserUserChoice");
 | |
|   rv = PrependRegistryValueName(valueName);
 | |
|   NS_ENSURE_SUCCESS_VOID(rv);
 | |
| 
 | |
|   nsCOMPtr<nsIWindowsRegKey> regKey =
 | |
|       do_CreateInstance("@mozilla.org/windows-registry-key;1", &rv);
 | |
|   NS_ENSURE_SUCCESS_VOID(rv);
 | |
| 
 | |
|   nsAutoString keyName;
 | |
|   keyName.AppendLiteral(DEFAULT_BROWSER_AGENT_KEY_NAME);
 | |
|   rv = regKey->Create(nsIWindowsRegKey::ROOT_KEY_CURRENT_USER, keyName,
 | |
|                       nsIWindowsRegKey::ACCESS_WRITE);
 | |
| 
 | |
|   bool prefVal = Preferences::GetBool(aPref, true);
 | |
| 
 | |
|   rv = regKey->WriteIntValue(valueName, prefVal);
 | |
|   NS_ENSURE_SUCCESS_VOID(rv);
 | |
| }
 | |
| 
 | |
| static void OnDefaultAgentRemoteSettingsPrefChanged(const char* aPref,
 | |
|                                                     void* aData) {
 | |
|   nsresult rv;
 | |
|   nsAutoString valueName;
 | |
|   if (strcmp(aPref, kPrefServicesSettingsServer) == 0) {
 | |
|     valueName.AssignLiteral("ServicesSettingsServer");
 | |
|   } else {
 | |
|     return;
 | |
|   }
 | |
|   rv = PrependRegistryValueName(valueName);
 | |
|   NS_ENSURE_SUCCESS_VOID(rv);
 | |
| 
 | |
|   nsCOMPtr<nsIWindowsRegKey> regKey =
 | |
|       do_CreateInstance("@mozilla.org/windows-registry-key;1", &rv);
 | |
|   NS_ENSURE_SUCCESS_VOID(rv);
 | |
| 
 | |
|   nsAutoString keyName;
 | |
|   keyName.AppendLiteral(DEFAULT_BROWSER_AGENT_KEY_NAME);
 | |
|   rv = regKey->Create(nsIWindowsRegKey::ROOT_KEY_CURRENT_USER, keyName,
 | |
|                       nsIWindowsRegKey::ACCESS_WRITE);
 | |
| 
 | |
|   NS_ENSURE_SUCCESS_VOID(rv);
 | |
| 
 | |
|   nsAutoString prefVal;
 | |
|   rv = Preferences::GetString(aPref, prefVal);
 | |
|   if (NS_FAILED(rv)) {
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   if (prefVal.IsEmpty()) {
 | |
|     rv = regKey->RemoveValue(valueName);
 | |
|   } else {
 | |
|     rv = regKey->WriteStringValue(valueName, prefVal);
 | |
|   }
 | |
|   NS_ENSURE_SUCCESS_VOID(rv);
 | |
| }
 | |
| 
 | |
| static void SetDefaultAgentLastRunTime() {
 | |
|   nsresult rv;
 | |
|   nsAutoString valueName;
 | |
|   valueName.AppendLiteral("AppLastRunTime");
 | |
|   rv = PrependRegistryValueName(valueName);
 | |
|   NS_ENSURE_SUCCESS_VOID(rv);
 | |
| 
 | |
|   nsCOMPtr<nsIWindowsRegKey> regKey =
 | |
|       do_CreateInstance("@mozilla.org/windows-registry-key;1", &rv);
 | |
|   NS_ENSURE_SUCCESS_VOID(rv);
 | |
| 
 | |
|   nsAutoString keyName;
 | |
|   keyName.AppendLiteral(DEFAULT_BROWSER_AGENT_KEY_NAME);
 | |
|   rv = regKey->Create(nsIWindowsRegKey::ROOT_KEY_CURRENT_USER, keyName,
 | |
|                       nsIWindowsRegKey::ACCESS_WRITE);
 | |
|   NS_ENSURE_SUCCESS_VOID(rv);
 | |
| 
 | |
|   FILETIME fileTime;
 | |
|   GetSystemTimeAsFileTime(&fileTime);
 | |
| 
 | |
|   ULARGE_INTEGER integerTime;
 | |
|   integerTime.u.LowPart = fileTime.dwLowDateTime;
 | |
|   integerTime.u.HighPart = fileTime.dwHighDateTime;
 | |
| 
 | |
|   rv = regKey->WriteInt64Value(valueName, integerTime.QuadPart);
 | |
|   NS_ENSURE_SUCCESS_VOID(rv);
 | |
| }
 | |
| 
 | |
| #  endif  // defined(MOZ_DEFAULT_BROWSER_AGENT)
 | |
| 
 | |
| #endif  // XP_WIN
 | |
| 
 | |
| void UnlockProfile() {
 | |
|   if (gProfileLock) {
 | |
|     gProfileLock->Unlock();
 | |
|   }
 | |
| }
 | |
| 
 | |
| nsresult LaunchChild(bool aBlankCommandLine, bool aTryExec) {
 | |
|   // Restart this process by exec'ing it into the current process
 | |
|   // if supported by the platform.  Otherwise, use NSPR.
 | |
| 
 | |
| #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<nsIFile> lf;
 | |
|   nsresult rv = XRE_GetBinaryPath(getter_AddRefs(lf));
 | |
|   if (NS_FAILED(rv)) return rv;
 | |
| 
 | |
| #    if defined(XP_WIN)
 | |
|   nsAutoString exePath;
 | |
|   rv = lf->GetPath(exePath);
 | |
|   if (NS_FAILED(rv)) return rv;
 | |
| 
 | |
|   HANDLE hProcess;
 | |
|   if (!WinLaunchChild(exePath.get(), gRestartArgc, gRestartArgv, nullptr,
 | |
|                       &hProcess))
 | |
|     return NS_ERROR_FAILURE;
 | |
|   // Keep the current process around until the restarted process has created
 | |
|   // its message queue, to avoid the launched process's windows being forced
 | |
|   // into the background.
 | |
|   mozilla::WaitForInputIdle(hProcess);
 | |
|   ::CloseHandle(hProcess);
 | |
| 
 | |
| #    else
 | |
|   nsAutoCString exePath;
 | |
|   rv = lf->GetNativePath(exePath);
 | |
|   if (NS_FAILED(rv)) return rv;
 | |
| 
 | |
| #      if defined(XP_UNIX)
 | |
|   if (aTryExec) {
 | |
|     execv(exePath.get(), gRestartArgv);
 | |
| 
 | |
|     // If execv returns we know it's because it failed.
 | |
|     return NS_ERROR_FAILURE;
 | |
|   }
 | |
| #      endif
 | |
|   if (PR_CreateProcessDetached(exePath.get(), gRestartArgv, nullptr, nullptr) ==
 | |
|       PR_FAILURE) {
 | |
|     return NS_ERROR_FAILURE;
 | |
|   }
 | |
| 
 | |
|   // Note that we don't know if the child process starts okay, if it
 | |
|   // immediately returns non-zero then we may mask that by returning a zero
 | |
|   // exit status.
 | |
| 
 | |
| #    endif  // 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.
 | |
|     printf_stderr(
 | |
|         "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<nsIStringBundleService> sbs =
 | |
|         mozilla::components::StringBundle::Service();
 | |
|     NS_ENSURE_TRUE(sbs, NS_ERROR_FAILURE);
 | |
| 
 | |
|     nsCOMPtr<nsIStringBundle> sb;
 | |
|     sbs->CreateBundle(kProfileProperties, getter_AddRefs(sb));
 | |
|     NS_ENSURE_TRUE_LOG(sbs, NS_ERROR_FAILURE);
 | |
| 
 | |
|     NS_ConvertUTF8toUTF16 appName(gAppData->name);
 | |
|     AutoTArray<nsString, 2> params = {appName, appName};
 | |
| 
 | |
|     // profileMissing
 | |
|     nsAutoString missingMessage;
 | |
|     rv = sb->FormatStringFromName("profileMissing", params, missingMessage);
 | |
|     NS_ENSURE_SUCCESS(rv, NS_ERROR_ABORT);
 | |
| 
 | |
|     nsAutoString missingTitle;
 | |
|     params.SetLength(1);
 | |
|     rv = sb->FormatStringFromName("profileMissingTitle", params, missingTitle);
 | |
|     NS_ENSURE_SUCCESS(rv, NS_ERROR_ABORT);
 | |
| 
 | |
|     nsCOMPtr<nsIPromptService> ps(do_GetService(NS_PROMPTSERVICE_CONTRACTID));
 | |
|     NS_ENSURE_TRUE(ps, NS_ERROR_FAILURE);
 | |
| 
 | |
|     ps->Alert(nullptr, missingTitle.get(), missingMessage.get());
 | |
| 
 | |
|     return NS_ERROR_ABORT;
 | |
|   }
 | |
| #endif    // MOZ_WIDGET_ANDROID
 | |
| }
 | |
| 
 | |
| 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<nsIStringBundleService> sbs =
 | |
|         mozilla::components::StringBundle::Service();
 | |
|     NS_ENSURE_TRUE(sbs, NS_ERROR_FAILURE);
 | |
| 
 | |
|     nsCOMPtr<nsIStringBundle> sb;
 | |
|     sbs->CreateBundle(kProfileProperties, getter_AddRefs(sb));
 | |
|     NS_ENSURE_TRUE_LOG(sbs, NS_ERROR_FAILURE);
 | |
| 
 | |
|     NS_ConvertUTF8toUTF16 appName(gAppData->name);
 | |
|     AutoTArray<nsString, 3> params = {appName, appName, appName};
 | |
| 
 | |
|     nsAutoString killMessage;
 | |
| #ifndef XP_MACOSX
 | |
|     rv = sb->FormatStringFromName(
 | |
|         aUnlocker ? "restartMessageUnlocker" : "restartMessageNoUnlocker2",
 | |
|         params, killMessage);
 | |
| #else
 | |
|     rv = sb->FormatStringFromName(
 | |
|         aUnlocker ? "restartMessageUnlockerMac" : "restartMessageNoUnlockerMac",
 | |
|         params, killMessage);
 | |
| #endif
 | |
|     NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
 | |
| 
 | |
|     params.SetLength(1);
 | |
|     nsAutoString killTitle;
 | |
|     rv = sb->FormatStringFromName("restartTitle", params, killTitle);
 | |
|     NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
 | |
| 
 | |
| #ifdef MOZ_BACKGROUNDTASKS
 | |
|     if (BackgroundTasks::IsBackgroundTaskMode()) {
 | |
|       // This error is handled specially to exit with a non-zero exit code.
 | |
|       printf_stderr("%s\n", NS_LossyConvertUTF16toASCII(killMessage).get());
 | |
|       return NS_ERROR_UNEXPECTED;
 | |
|     }
 | |
| #endif
 | |
| 
 | |
|     if (gfxPlatform::IsHeadless()) {
 | |
|       // TODO: make a way to turn off all dialogs when headless.
 | |
|       Output(true, "%s\n", NS_LossyConvertUTF16toASCII(killMessage).get());
 | |
|       return NS_ERROR_FAILURE;
 | |
|     }
 | |
| 
 | |
|     nsCOMPtr<nsIPromptService> ps(do_GetService(NS_PROMPTSERVICE_CONTRACTID));
 | |
|     NS_ENSURE_TRUE(ps, NS_ERROR_FAILURE);
 | |
| 
 | |
|     if (aUnlocker) {
 | |
|       int32_t button;
 | |
| #ifdef MOZ_WIDGET_ANDROID
 | |
|       // On Android we always kill the process if the lock is still being held
 | |
|       button = 0;
 | |
| #else
 | |
|       const uint32_t flags = (nsIPromptService::BUTTON_TITLE_IS_STRING *
 | |
|                               nsIPromptService::BUTTON_POS_0) +
 | |
|                              (nsIPromptService::BUTTON_TITLE_CANCEL *
 | |
|                               nsIPromptService::BUTTON_POS_1);
 | |
| 
 | |
|       bool checkState = false;
 | |
|       rv = ps->ConfirmEx(nullptr, killTitle.get(), killMessage.get(), flags,
 | |
|                          killTitle.get(), nullptr, nullptr, nullptr,
 | |
|                          &checkState, &button);
 | |
|       NS_ENSURE_SUCCESS_LOG(rv, rv);
 | |
| #endif
 | |
| 
 | |
|       if (button == 0) {
 | |
|         rv = aUnlocker->Unlock(nsIProfileUnlocker::FORCE_QUIT);
 | |
|         if (NS_FAILED(rv)) {
 | |
|           return rv;
 | |
|         }
 | |
| 
 | |
|         SaveFileToEnv("XRE_PROFILE_PATH", aProfileDir);
 | |
|         SaveFileToEnv("XRE_PROFILE_LOCAL_PATH", aProfileLocalDir);
 | |
| 
 | |
| #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<nsIFile> 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<nsIWindowWatcher> windowWatcher(
 | |
|           do_GetService(NS_WINDOWWATCHER_CONTRACTID));
 | |
|       nsCOMPtr<nsIDialogParamBlock> ioParamBlock(
 | |
|           do_CreateInstance(NS_DIALOGPARAMBLOCK_CONTRACTID));
 | |
|       nsCOMPtr<nsIMutableArray> dlgArray(
 | |
|           do_CreateInstance(NS_ARRAY_CONTRACTID));
 | |
|       NS_ENSURE_TRUE(windowWatcher && ioParamBlock && dlgArray,
 | |
|                      NS_ERROR_FAILURE);
 | |
| 
 | |
|       ioParamBlock->SetObjects(dlgArray);
 | |
| 
 | |
|       nsCOMPtr<nsIAppStartup> appStartup(components::AppStartup::Service());
 | |
|       NS_ENSURE_TRUE(appStartup, NS_ERROR_FAILURE);
 | |
| 
 | |
|       nsAutoCString features("centerscreen,chrome,modal,titlebar");
 | |
|       // If we're launching a private browsing window make sure to set that
 | |
|       // feature for the Profile Manager window as well, so it groups correctly
 | |
|       // on the Windows taskbar.
 | |
|       if (CheckArgExists("private-window") == ARG_FOUND) {
 | |
|         features.AppendLiteral(",private");
 | |
|       }
 | |
|       nsCOMPtr<mozIDOMWindowProxy> newWindow;
 | |
|       rv = windowWatcher->OpenWindow(
 | |
|           nullptr, nsDependentCString(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<char*>("-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<nsIToolkitProfile> gResetOldProfile;
 | |
| 
 | |
| static nsresult LockProfile(nsINativeAppSupport* aNative, nsIFile* aRootDir,
 | |
|                             nsIFile* aLocalDir, nsIToolkitProfile* aProfile,
 | |
|                             nsIProfileLock** aResult) {
 | |
|   // If you close Firefox and very quickly reopen it, the old Firefox may
 | |
|   // still be closing down. Rather than immediately showing the
 | |
|   // "Firefox is running but is not responding" message, we spend a few
 | |
|   // seconds retrying first.
 | |
| 
 | |
|   static const int kLockRetrySeconds = 5;
 | |
|   static const int kLockRetrySleepMS = 100;
 | |
| 
 | |
|   nsresult rv;
 | |
|   nsCOMPtr<nsIProfileUnlocker> unlocker;
 | |
|   const TimeStamp start = TimeStamp::Now();
 | |
|   do {
 | |
|     if (aProfile) {
 | |
|       rv = aProfile->Lock(getter_AddRefs(unlocker), aResult);
 | |
|     } else {
 | |
|       rv = NS_LockProfilePath(aRootDir, aLocalDir, getter_AddRefs(unlocker),
 | |
|                               aResult);
 | |
|     }
 | |
|     if (NS_SUCCEEDED(rv)) {
 | |
|       StartupTimeline::Record(StartupTimeline::AFTER_PROFILE_LOCKED);
 | |
|       return NS_OK;
 | |
|     }
 | |
|     PR_Sleep(kLockRetrySleepMS);
 | |
|   } while (TimeStamp::Now() - start <
 | |
|            TimeDuration::FromSeconds(kLockRetrySeconds));
 | |
| 
 | |
|   return ProfileLockedDialog(aRootDir, aLocalDir, unlocker, aNative, aResult);
 | |
| }
 | |
| 
 | |
| // Pick a profile. We need to end up with a profile root dir, local dir and
 | |
| // potentially an nsIToolkitProfile instance.
 | |
| //
 | |
| // 1) check for --profile <path>
 | |
| // 2) check for -P <name>
 | |
| // 3) check for --ProfileManager
 | |
| // 4) use the default profile, if there is one
 | |
| // 5) if there are *no* profiles, set up profile-migration
 | |
| // 6) display the profile-manager UI
 | |
| 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<const char>& aStr) final {
 | |
|     fprintf(mFile, "%.*s", int(aStr.size()), aStr.data());
 | |
|   }
 | |
| };
 | |
| 
 | |
| static void SubmitDowngradeTelemetry(const nsCString& aLastVersion,
 | |
|                                      bool aHasSync, int32_t aButton) {
 | |
|   nsCOMPtr<nsIPrefService> prefSvc =
 | |
|       do_GetService("@mozilla.org/preferences-service;1");
 | |
|   NS_ENSURE_TRUE_VOID(prefSvc);
 | |
| 
 | |
|   nsCOMPtr<nsIPrefBranch> prefBranch = do_QueryInterface(prefSvc);
 | |
|   NS_ENSURE_TRUE_VOID(prefBranch);
 | |
| 
 | |
|   bool enabled;
 | |
|   nsresult rv =
 | |
|       prefBranch->GetBoolPref(kPrefHealthReportUploadEnabled, &enabled);
 | |
|   NS_ENSURE_SUCCESS_VOID(rv);
 | |
|   if (!enabled) {
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   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<nsIPropertyBag2> 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<nsIFile> 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<nsIFile> pingSender;
 | |
|   rv = NS_GetSpecialDirectory(NS_GRE_BIN_DIR, getter_AddRefs(pingSender));
 | |
|   NS_ENSURE_SUCCESS_VOID(rv);
 | |
| #  ifdef XP_WIN
 | |
|   pingSender->Append(u"pingsender.exe"_ns);
 | |
| #  else
 | |
|   pingSender->Append(u"pingsender"_ns);
 | |
| #  endif
 | |
| 
 | |
|   bool exists;
 | |
|   rv = pingSender->Exists(&exists);
 | |
|   NS_ENSURE_SUCCESS_VOID(rv);
 | |
|   if (!exists) {
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   FILE* file;
 | |
|   rv = pingFile->OpenANSIFileDesc("w", &file);
 | |
|   NS_ENSURE_SUCCESS_VOID(rv);
 | |
| 
 | |
|   JSONWriter w(MakeUnique<FileWriteFunc>(file));
 | |
|   w.Start();
 | |
|   {
 | |
|     w.StringProperty("type",
 | |
|                      Span<const char>(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<const char*>(gAppData->buildID)));
 | |
|       w.StringProperty(
 | |
|           "name", MakeStringSpan(static_cast<const char*>(gAppData->name)));
 | |
|       w.StringProperty(
 | |
|           "version",
 | |
|           MakeStringSpan(static_cast<const char*>(gAppData->version)));
 | |
|       w.StringProperty("displayVersion",
 | |
|                        MOZ_STRINGIFY(MOZ_APP_VERSION_DISPLAY));
 | |
|       w.StringProperty(
 | |
|           "vendor", MakeStringSpan(static_cast<const char*>(gAppData->vendor)));
 | |
|       w.StringProperty("platformVersion", gToolkitVersion);
 | |
| #  ifdef TARGET_XPCOM_ABI
 | |
|       w.StringProperty("xpcomAbi", TARGET_XPCOM_ABI);
 | |
| #  else
 | |
|       w.StringProperty("xpcomAbi", "unknown");
 | |
| #  endif
 | |
|       w.StringProperty("channel", 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<nsIProcess> process =
 | |
|       do_CreateInstance("@mozilla.org/process/util;1");
 | |
|   NS_ENSURE_TRUE_VOID(process);
 | |
|   process->Init(pingSender);
 | |
|   process->SetStartHidden(true);
 | |
|   process->SetNoShell(true);
 | |
| 
 | |
| #  ifdef XP_WIN
 | |
|   process->Runw(false, args, 2);
 | |
| #  else
 | |
|   process->Run(false, args, 2);
 | |
| #  endif
 | |
| }
 | |
| 
 | |
| static const char kProfileDowngradeURL[] =
 | |
|     "chrome://mozapps/content/profile/profileDowngrade.xhtml";
 | |
| 
 | |
| 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<nsIPrefService> prefSvc =
 | |
|           do_GetService("@mozilla.org/preferences-service;1");
 | |
|       NS_ENSURE_TRUE(prefSvc, rv);
 | |
| 
 | |
|       nsCOMPtr<nsIFile> prefsFile;
 | |
|       rv = aProfileDir->Clone(getter_AddRefs(prefsFile));
 | |
|       NS_ENSURE_SUCCESS(rv, rv);
 | |
| 
 | |
|       rv = prefsFile->Append(u"prefs.js"_ns);
 | |
|       NS_ENSURE_SUCCESS(rv, rv);
 | |
| 
 | |
|       rv = prefSvc->ReadUserPrefsFromFile(prefsFile);
 | |
|       if (NS_SUCCEEDED(rv)) {
 | |
|         nsCOMPtr<nsIPrefBranch> prefBranch = do_QueryInterface(prefSvc);
 | |
| 
 | |
|         rv = prefBranch->PrefHasUserValue("services.sync.username", &hasSync);
 | |
|         NS_ENSURE_SUCCESS(rv, rv);
 | |
|       }
 | |
| 
 | |
|       nsCOMPtr<nsIWindowWatcher> windowWatcher =
 | |
|           do_GetService(NS_WINDOWWATCHER_CONTRACTID);
 | |
|       NS_ENSURE_TRUE(windowWatcher, NS_ERROR_ABORT);
 | |
| 
 | |
|       nsCOMPtr<nsIAppStartup> appStartup(components::AppStartup::Service());
 | |
|       NS_ENSURE_TRUE(appStartup, NS_ERROR_FAILURE);
 | |
| 
 | |
|       nsCOMPtr<nsIDialogParamBlock> paramBlock =
 | |
|           do_CreateInstance(NS_DIALOGPARAMBLOCK_CONTRACTID);
 | |
|       NS_ENSURE_TRUE(paramBlock, NS_ERROR_ABORT);
 | |
| 
 | |
|       uint8_t flags = 0;
 | |
|       if (hasSync) {
 | |
|         flags |= nsIToolkitProfileService::hasSync;
 | |
|       }
 | |
| 
 | |
|       paramBlock->SetInt(0, flags);
 | |
| 
 | |
|       nsAutoCString features("centerscreen,chrome,modal,titlebar");
 | |
|       // If we're launching a private browsing window make sure to set that
 | |
|       // feature for the Profile Manager window as well, so it groups correctly
 | |
|       // on the Windows taskbar.
 | |
|       if (CheckArgExists("private-window") == ARG_FOUND) {
 | |
|         features.AppendLiteral(",private");
 | |
|       }
 | |
|       nsCOMPtr<mozIDOMWindowProxy> newWindow;
 | |
|       rv = windowWatcher->OpenWindow(
 | |
|           nullptr, nsDependentCString(kProfileDowngradeURL), "_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<nsIToolkitProfile> 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<nsIFile> 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
 | |
|  * "<appversion>_<appbuildid>/<platformbuildid>". 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<nsIFile> 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<nsIFile> 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<nsIFile> 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<nsIFile> 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<nsIFile> 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 <dwrite.h>
 | |
| #  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<IUnknown**>(&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<nsINativeAppSupport> mNativeApp;
 | |
|   RefPtr<nsToolkitProfileService> mProfileSvc;
 | |
|   nsCOMPtr<nsIFile> mProfD;
 | |
|   nsCOMPtr<nsIFile> mProfLD;
 | |
|   nsCOMPtr<nsIProfileLock> mProfileLock;
 | |
| #if defined(MOZ_HAS_REMOTE)
 | |
|   RefPtr<nsRemoteService> mRemoteService;
 | |
| #endif
 | |
| 
 | |
|   UniquePtr<ScopedXPCOMStartup> mScopedXPCOM;
 | |
|   UniquePtr<XREAppData> 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<int>(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.
 | |
| #  ifdef XP_DARWIN
 | |
|   // We use Mach IPC for shared memory, so a lower limit suffices.
 | |
|   // See also the Darwin case in config/external/nspr/pr/moz.build
 | |
|   static constexpr rlim_t kFDs = 4096;
 | |
| #  else   // Unix but not Darwin
 | |
|   // This can be increased if needed, but also be aware that Linux
 | |
|   // distributions may impose hard limits less than this.
 | |
|   static constexpr rlim_t kFDs = 65536;
 | |
| #  endif  // Darwin or not
 | |
|   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<LPBYTE>(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<LPBYTE>(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<int>(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<nsCString> 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<ChaosFeature>(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<nsIFile> 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<nsIFile> xreBinDirectory;
 | |
|   xreBinDirectory = mDirProvider.GetGREBinDir();
 | |
| 
 | |
|   if ((mAppData->flags & NS_XRE_ENABLE_CRASH_REPORTER) &&
 | |
|       NS_SUCCEEDED(CrashReporter::SetExceptionHandler(xreBinDirectory))) {
 | |
|     nsCOMPtr<nsIFile> 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<nsCString> 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<nsIFile> 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<nsIFile> 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<char*>("-override");
 | |
|     gRestartArgv[gRestartArgc++] = const_cast<char*>(override);
 | |
|   }
 | |
| 
 | |
|   gRestartArgv[gRestartArgc] = nullptr;
 | |
| 
 | |
|   Maybe<bool> 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;
 | |
|   }
 | |
| 
 | |
| #ifdef MOZ_ENABLE_DBUS
 | |
|   const char* dbusServiceLauncher = nullptr;
 | |
|   ar = CheckArg("dbus-service", &dbusServiceLauncher, CheckArgFlag::None);
 | |
|   if (ar == ARG_BAD) {
 | |
|     Output(true, "Missing launcher param for --dbus-service\n");
 | |
|     return 1;
 | |
|   }
 | |
|   if (ar == ARG_FOUND) {
 | |
|     UniquePtr<DBusService> dbusService =
 | |
|         MakeUnique<DBusService>(dbusServiceLauncher);
 | |
|     if (dbusService->Init()) {
 | |
|       dbusService->Run();
 | |
|     }
 | |
|     *aExitFlag = true;
 | |
|     return 0;
 | |
|   }
 | |
| #endif
 | |
| 
 | |
|   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<wchar_t[]> greDir(static_cast<wchar_t*>(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<ShouldNotProcessUpdatesReason> 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<nsCString> 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<nsIFile> 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<nsCOMPtr<nsIFile>, nsresult> GetIncompleteStartupFile(nsIFile* aProfLD) {
 | |
|   nsCOMPtr<nsIFile> 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<nsCOMPtr<nsIFile>, 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<nsIToolkitProfile> 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
 | |
|       auto& startupToken =
 | |
|           GdkIsWaylandDisplay() ? mXDGActivationToken : mDesktopStartupID;
 | |
| #    ifdef MOZ_X11
 | |
|       if (GdkIsX11Display() && startupToken.IsEmpty())
 | |
|         startupToken = SynthesizeStartupToken();
 | |
| #    endif /* MOZ_X11 */
 | |
| #  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 <param> 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<nsUpdateProcessor> 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> shouldNotProcessUpdatesReason =
 | |
|       ShouldNotProcessUpdates(mDirProvider);
 | |
|   if (shouldNotProcessUpdatesReason.isNothing()) {
 | |
|     // Check for and process any available updates
 | |
|     nsCOMPtr<nsIFile> 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<nsIFile> 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<nsIFile> 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")) {
 | |
| #  ifdef XP_MACOSX
 | |
|     InitializeMacApp();
 | |
| #  endif
 | |
|     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<mozilla::DllServices> 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<nsIAppStartup> appStartup;
 | |
|   // Ditto with the command line.
 | |
|   nsCOMPtr<nsICommandLineRunner> 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<nsIPrefService> prefs =
 | |
|         do_GetService("@mozilla.org/preferences-service;1", &rv);
 | |
|     if (NS_SUCCEEDED(rv)) {
 | |
|       nsCOMPtr<nsIPrefBranch> 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<nsIIOService> 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<nsIFile> 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<nsIFile> 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<nsIProfileMigrator> 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<nsIFile> 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<nsIObserverService> 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<char**>(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<nsIObserverService> 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<bool> 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<nsIPowerManagerService> powerManagerService =
 | |
|         do_GetService(POWERMANAGERSERVICE_CONTRACTID);
 | |
|     nsCOMPtr<nsIStringBundleService> 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<nsIFile> 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<nsIFile> 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<XREAppData>(*aConfig.appData);
 | |
|   } else {
 | |
|     MOZ_RELEASE_ASSERT(aConfig.appDataPath);
 | |
|     nsCOMPtr<nsIFile> 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<XREAppData>();
 | |
|     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<nsIFile> 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<nsIFile> 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<nsIFile> parent;
 | |
|     greDir->GetParent(getter_AddRefs(parent));
 | |
|     greDir = parent.forget();
 | |
|     greDir->AppendNative("Resources"_ns);
 | |
| #endif
 | |
| 
 | |
|     mAppData->xreDirectory = greDir;
 | |
|   }
 | |
| 
 | |
| #if defined(MOZ_WIDGET_ANDROID)
 | |
|   nsCOMPtr<nsIFile> 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<bool> result = WindowsBCryptInitialization();
 | |
|     MOZ_ASSERT(result);
 | |
|   }
 | |
| 
 | |
| #  if defined(_M_IX86) || defined(_M_X64)
 | |
|   {
 | |
|     DebugOnly<bool> 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<nsIFile> 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<ScopedXPCOMStartup>();
 | |
| 
 | |
|   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<nsIFile> 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<nsIFile> 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<nsIFile> 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
 |