зеркало из https://github.com/mozilla/gecko-dev.git
5760 строки
176 KiB
C++
5760 строки
176 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/Likely.h"
|
|
#include "mozilla/MemoryChecking.h"
|
|
#include "mozilla/Poison.h"
|
|
#include "mozilla/Preferences.h"
|
|
#include "mozilla/Printf.h"
|
|
#include "mozilla/ResultExtensions.h"
|
|
#include "mozilla/ScopeExit.h"
|
|
#include "mozilla/StaticPrefs_fission.h"
|
|
#include "mozilla/Telemetry.h"
|
|
#include "mozilla/Utf8.h"
|
|
#include "mozilla/intl/LocaleService.h"
|
|
#include "mozilla/JSONWriter.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"
|
|
#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"
|
|
// 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 "nsAppStartupNotifier.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 "nsIUUIDGenerator.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 "mozilla/Unused.h"
|
|
|
|
#ifdef XP_WIN
|
|
# include "nsIWinAppHelper.h"
|
|
# include <windows.h>
|
|
# include <intrin.h>
|
|
# include <math.h>
|
|
# include "cairo/cairo-features.h"
|
|
# include "mozilla/PreXULSkeletonUI.h"
|
|
# include "mozilla/DllPrefetchExperimentRegistryInfo.h"
|
|
# include "mozilla/WindowsDllBlocklist.h"
|
|
# include "mozilla/WindowsProcessMitigations.h"
|
|
# include "mozilla/WinHeaderOnlyUtils.h"
|
|
# include "mozilla/mscom/ProcessRuntime.h"
|
|
# if defined(MOZ_GECKO_PROFILER)
|
|
# include "mozilla/mscom/ProfilerMarkers.h"
|
|
# endif // defined(MOZ_GECKO_PROFILER)
|
|
# include "mozilla/widget/AudioSession.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"
|
|
# if (defined(XP_WIN) || defined(XP_MACOSX))
|
|
# include "nsIUUIDGenerator.h"
|
|
# endif
|
|
#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 "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 <comdef.h>
|
|
# include <wbemidl.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"
|
|
#define NS_CRASHREPORTER_CONTRACTID "@mozilla.org/toolkit/crash-reporter;1"
|
|
#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"
|
|
# include "sandboxPermissions.h"
|
|
# endif
|
|
#endif
|
|
|
|
#ifdef MOZ_CODE_COVERAGE
|
|
# include "mozilla/CodeCoverageHandler.h"
|
|
#endif
|
|
|
|
#include "mozilla/mozalloc_oom.h"
|
|
#include "SafeMode.h"
|
|
|
|
#ifdef MOZ_BACKGROUNDTASKS
|
|
# include "mozilla/BackgroundTasks.h"
|
|
# include "nsIPowerManagerService.h"
|
|
# include "nsIStringBundle.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 kPrefSecurityContentSignatureRootHash[] =
|
|
"security.content.signature.root_hash";
|
|
#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";
|
|
static const char kPrefDrawTabsInTitlebar[] = "browser.tabs.drawInTitlebar";
|
|
#endif // defined(XP_WIN)
|
|
|
|
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;
|
|
|
|
int gRestartArgc;
|
|
char** gRestartArgv;
|
|
|
|
// If gRestartedByOS is set, we were automatically restarted by the OS.
|
|
bool gRestartedByOS = false;
|
|
|
|
bool gIsGtest = false;
|
|
|
|
nsString gAbsoluteArgv0Path;
|
|
|
|
#if defined(XP_WIN)
|
|
nsString gProcessStartupShortcut;
|
|
#endif
|
|
|
|
#if defined(MOZ_WIDGET_GTK)
|
|
# include <glib.h>
|
|
# if defined(DEBUG) || defined(NS_BUILD_REFCNT_LOGGING)
|
|
# define CLEANUP_MEMORY 1
|
|
# define PANGO_ENABLE_BACKEND
|
|
# include <pango/pangofc-fontmap.h>
|
|
# endif
|
|
# include <gtk/gtk.h>
|
|
# ifdef MOZ_WAYLAND
|
|
# include <gdk/gdkwayland.h>
|
|
# include "mozilla/widget/nsWaylandDisplay.h"
|
|
# endif
|
|
# ifdef MOZ_X11
|
|
# include <gdk/gdkx.h>
|
|
# endif /* MOZ_X11 */
|
|
# include <fontconfig/fontconfig.h>
|
|
#endif
|
|
#include "BinaryPath.h"
|
|
|
|
#ifdef MOZ_LINKER
|
|
extern "C" MFBT_API bool IsSignalHandlingBroken();
|
|
#endif
|
|
|
|
#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.");
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 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);
|
|
}
|
|
#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;
|
|
|
|
// 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 fission experiment enrollment status set during the previous run, which
|
|
// is controlled by the following preferences:
|
|
//
|
|
// 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 kPrefFissionExperimentEnrollmentStatus[] =
|
|
"fission.experiment.enrollmentStatus";
|
|
//
|
|
// The enrollment status to be used at browser startup. This automatically
|
|
// synced from the above enrollmentStatus preference whenever the latter is
|
|
// changed. It can have any of the values defined in the
|
|
// `nsIXULRuntime_ExperimentStatus` enum. Meanings are documented in
|
|
// the declaration of `nsIXULRuntime.fissionExperimentStatus`
|
|
static const char kPrefFissionExperimentStartupEnrollmentStatus[] =
|
|
"fission.experiment.startupEnrollmentStatus";
|
|
|
|
// 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";
|
|
|
|
static nsIXULRuntime::ExperimentStatus gFissionExperimentStatus =
|
|
nsIXULRuntime::eExperimentStatusUnenrolled;
|
|
static bool gFissionAutostart = false;
|
|
static bool gFissionAutostartInitialized = false;
|
|
static nsIXULRuntime::FissionDecisionStatus gFissionDecisionStatus;
|
|
|
|
enum E10sStatus {
|
|
kE10sEnabledByDefault,
|
|
kE10sDisabledByUser,
|
|
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;
|
|
}
|
|
|
|
#if defined(MOZILLA_OFFICIAL) && MOZ_BUILD_APP_IS_BROWSER
|
|
bool allowSingleProcessOutsideAutomation = false;
|
|
#else
|
|
bool allowSingleProcessOutsideAutomation = true;
|
|
#endif
|
|
|
|
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 tests switch to environment variables
|
|
// to disable e10s and then we can get rid of this.
|
|
if (allowSingleProcessOutsideAutomation ||
|
|
xpc::AreNonLocalConnectionsDisabled()) {
|
|
bool optInPref =
|
|
Preferences::GetBool("browser.tabs.remote.autostart", true);
|
|
|
|
if (optInPref) {
|
|
gBrowserTabsRemoteAutostart = true;
|
|
} else {
|
|
status = kE10sDisabledByUser;
|
|
}
|
|
} else {
|
|
gBrowserTabsRemoteAutostart = true;
|
|
}
|
|
|
|
// Uber override pref for emergency blocking
|
|
if (gBrowserTabsRemoteAutostart) {
|
|
const char* forceDisable = PR_GetEnv("MOZ_FORCE_DISABLE_E10S");
|
|
#if defined(MOZ_WIDGET_ANDROID)
|
|
// We need this for xpcshell on Android
|
|
if (forceDisable && *forceDisable) {
|
|
#else
|
|
// The environment variable must match the application version to apply.
|
|
if (forceDisable && gAppData && !strcmp(forceDisable, gAppData->version)) {
|
|
#endif
|
|
gBrowserTabsRemoteAutostart = false;
|
|
status = kE10sForceDisabled;
|
|
}
|
|
}
|
|
|
|
gBrowserTabsRemoteStatus = status;
|
|
|
|
return gBrowserTabsRemoteAutostart;
|
|
}
|
|
|
|
} // namespace mozilla
|
|
|
|
static bool FissionExperimentEnrolled() {
|
|
MOZ_ASSERT(XRE_IsParentProcess());
|
|
return gFissionExperimentStatus == nsIXULRuntime::eExperimentStatusControl ||
|
|
gFissionExperimentStatus == nsIXULRuntime::eExperimentStatusTreatment;
|
|
}
|
|
|
|
static void FissionExperimentDisqualify() {
|
|
MOZ_ASSERT(XRE_IsParentProcess());
|
|
// Setting this pref's user value will be detected by Normandy, causing the
|
|
// client to be unenrolled from the experiment.
|
|
Preferences::SetUint(kPrefFissionExperimentEnrollmentStatus,
|
|
nsIXULRuntime::eExperimentStatusDisqualified);
|
|
}
|
|
|
|
static void OnFissionEnrollmentStatusChanged(const char* aPref, void* aData) {
|
|
Preferences::SetUint(
|
|
kPrefFissionExperimentStartupEnrollmentStatus,
|
|
Preferences::GetUint(kPrefFissionExperimentEnrollmentStatus,
|
|
nsIXULRuntime::eExperimentStatusUnenrolled));
|
|
}
|
|
|
|
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 FissionAutostart was initialized.
|
|
class FissionEnrollmentStatusShutdownObserver 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));
|
|
OnFissionEnrollmentStatusChanged(kPrefFissionExperimentEnrollmentStatus,
|
|
nullptr);
|
|
return NS_OK;
|
|
}
|
|
|
|
private:
|
|
~FissionEnrollmentStatusShutdownObserver() = default;
|
|
};
|
|
NS_IMPL_ISUPPORTS(FissionEnrollmentStatusShutdownObserver, nsIObserver)
|
|
} // namespace
|
|
|
|
static void OnFissionAutostartChanged(const char* aPref, void* aData) {
|
|
MOZ_ASSERT(FissionExperimentEnrolled());
|
|
if (Preferences::HasUserValue(kPrefFissionAutostart)) {
|
|
FissionExperimentDisqualify();
|
|
}
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
// Initialize the fission experiment, configuring fission.autostart's
|
|
// default, before checking other overrides. This allows opting-out of a
|
|
// fission experiment through about:preferences or about:config from a
|
|
// safemode session.
|
|
uint32_t experimentRaw =
|
|
Preferences::GetUint(kPrefFissionExperimentStartupEnrollmentStatus,
|
|
nsIXULRuntime::eExperimentStatusUnenrolled);
|
|
gFissionExperimentStatus =
|
|
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(&OnFissionEnrollmentStatusChanged,
|
|
kPrefFissionExperimentEnrollmentStatus);
|
|
if (nsCOMPtr<nsIObserverService> observerService =
|
|
mozilla::services::GetObserverService()) {
|
|
nsCOMPtr<nsIObserver> shutdownObserver =
|
|
new FissionEnrollmentStatusShutdownObserver();
|
|
observerService->AddObserver(shutdownObserver, "profile-before-change",
|
|
false);
|
|
}
|
|
|
|
// If the user has overridden an active experiment by setting a user value for
|
|
// "fission.autostart", disqualify the user from the experiment.
|
|
if (Preferences::HasUserValue(kPrefFissionAutostart) &&
|
|
FissionExperimentEnrolled()) {
|
|
FissionExperimentDisqualify();
|
|
gFissionExperimentStatus = nsIXULRuntime::eExperimentStatusDisqualified;
|
|
}
|
|
|
|
// Configure the default branch for "fission.autostart" based on experiment
|
|
// enrollment status.
|
|
if (FissionExperimentEnrolled()) {
|
|
bool isTreatment =
|
|
gFissionExperimentStatus == nsIXULRuntime::eExperimentStatusTreatment;
|
|
Preferences::SetBool(kPrefFissionAutostart, isTreatment,
|
|
PrefValueKind::Default);
|
|
}
|
|
|
|
if (!BrowserTabsRemoteAutostart()) {
|
|
gFissionAutostart = false;
|
|
if (gBrowserTabsRemoteStatus == kE10sForceDisabled) {
|
|
gFissionDecisionStatus = nsIXULRuntime::eFissionDisabledByE10sEnv;
|
|
} else {
|
|
gFissionDecisionStatus = nsIXULRuntime::eFissionDisabledByE10sOther;
|
|
}
|
|
} else if (gSafeMode) {
|
|
gFissionAutostart = false;
|
|
gFissionDecisionStatus = nsIXULRuntime::eFissionDisabledBySafeMode;
|
|
} else if (EnvHasValue("MOZ_FORCE_ENABLE_FISSION")) {
|
|
gFissionAutostart = true;
|
|
gFissionDecisionStatus = nsIXULRuntime::eFissionEnabledByEnv;
|
|
} else {
|
|
// NOTE: This will take into account changes to the default due to
|
|
// `InitializeFissionExperimentStatus`.
|
|
gFissionAutostart = Preferences::GetBool(kPrefFissionAutostart, false);
|
|
if (gFissionExperimentStatus == nsIXULRuntime::eExperimentStatusControl) {
|
|
gFissionDecisionStatus = nsIXULRuntime::eFissionExperimentControl;
|
|
} else if (gFissionExperimentStatus ==
|
|
nsIXULRuntime::eExperimentStatusTreatment) {
|
|
gFissionDecisionStatus = nsIXULRuntime::eFissionExperimentTreatment;
|
|
} else 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);
|
|
|
|
// If we're actively enrolled in the fission experiment, disqualify the user
|
|
// from the experiment if the fission pref is modified.
|
|
if (FissionExperimentEnrolled()) {
|
|
Preferences::RegisterCallback(&OnFissionAutostartChanged,
|
|
kPrefFissionAutostart);
|
|
}
|
|
}
|
|
|
|
namespace mozilla {
|
|
|
|
bool FissionAutostart() {
|
|
EnsureFissionAutostartInitialized();
|
|
return gFissionAutostart;
|
|
}
|
|
|
|
bool SessionHistoryInParent() {
|
|
return FissionAutostart() ||
|
|
StaticPrefs::
|
|
fission_sessionHistoryInParent_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 SYNC_ENUMS(a, b) \
|
|
static_assert(nsIXULRuntime::PROCESS_TYPE_##a == \
|
|
static_cast<int>(GeckoProcessType_##b), \
|
|
"GeckoProcessType in nsXULAppAPI.h not synchronized with " \
|
|
"nsIXULRuntime.idl");
|
|
|
|
SYNC_ENUMS(DEFAULT, Default)
|
|
SYNC_ENUMS(PLUGIN, Plugin)
|
|
SYNC_ENUMS(CONTENT, Content)
|
|
SYNC_ENUMS(IPDLUNITTEST, IPDLUnitTest)
|
|
SYNC_ENUMS(GMPLUGIN, GMPlugin)
|
|
SYNC_ENUMS(GPU, GPU)
|
|
SYNC_ENUMS(VR, VR)
|
|
SYNC_ENUMS(RDD, RDD)
|
|
SYNC_ENUMS(SOCKET, Socket)
|
|
SYNC_ENUMS(SANDBOX_BROKER, RemoteSandboxBroker)
|
|
SYNC_ENUMS(FORKSERVER, ForkServer)
|
|
|
|
// .. and ensure that that is all of them:
|
|
static_assert(GeckoProcessType_ForkServer + 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::GetFissionExperimentStatus(ExperimentStatus* aResult) {
|
|
if (!XRE_IsParentProcess()) {
|
|
return NS_ERROR_NOT_AVAILABLE;
|
|
}
|
|
|
|
EnsureFissionAutostartInitialized();
|
|
*aResult = gFissionExperimentStatus;
|
|
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 eFissionDisabledBySafeMode:
|
|
aResult = "disabledBySafeMode";
|
|
break;
|
|
case eFissionEnabledByDefault:
|
|
aResult = "enabledByDefault";
|
|
break;
|
|
case eFissionDisabledByDefault:
|
|
aResult = "disabledByDefault";
|
|
break;
|
|
case eFissionEnabledByUserPref:
|
|
aResult = "enabledByUserPref";
|
|
break;
|
|
case eFissionDisabledByUserPref:
|
|
aResult = "disabledByUserPref";
|
|
break;
|
|
case eFissionDisabledByE10sOther:
|
|
aResult = "disabledByE10sOther";
|
|
break;
|
|
default:
|
|
MOZ_ASSERT_UNREACHABLE("Unexpected enum value");
|
|
}
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsXULAppInfo::GetSessionHistoryInParent(bool* aResult) {
|
|
*aResult = SessionHistoryInParent();
|
|
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::GetAccessibleHandlerUsed(bool* aResult) {
|
|
#if defined(ACCESSIBILITY) && defined(XP_WIN)
|
|
*aResult = Preferences::GetBool("accessibility.handler.enabled", false) &&
|
|
a11y::IsHandlerRegistered();
|
|
#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::GetShouldBlockIncompatJaws(bool* aResult) {
|
|
*aResult = false;
|
|
#if defined(ACCESSIBILITY) && defined(XP_WIN)
|
|
*aResult = mozilla::a11y::Compatibility::IsOldJAWS();
|
|
#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::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::GetIsReleaseOrBeta(bool* aResult) {
|
|
#ifdef RELEASE_OR_BETA
|
|
*aResult = true;
|
|
#else
|
|
*aResult = false;
|
|
#endif
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsXULAppInfo::GetIsOfficialBranding(bool* aResult) {
|
|
#ifdef MOZ_OFFICIAL_BRANDING
|
|
*aResult = true;
|
|
#else
|
|
*aResult = false;
|
|
#endif
|
|
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::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::GetEnabled(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;
|
|
}
|
|
|
|
return CrashReporter::AnnotateCrashReport(annotation, data);
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsXULAppInfo::RemoveCrashReportAnnotation(const nsACString& key) {
|
|
CrashReporter::Annotation annotation;
|
|
|
|
if (!AnnotationFromString(annotation, PromiseFlatCString(key).get())) {
|
|
return NS_ERROR_INVALID_ARG;
|
|
}
|
|
|
|
return CrashReporter::RemoveCrashReportAnnotation(annotation);
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsXULAppInfo::IsAnnotationWhitelistedForPing(const nsACString& aValue,
|
|
bool* aIsWhitelisted) {
|
|
CrashReporter::Annotation annotation;
|
|
|
|
if (!AnnotationFromString(annotation, PromiseFlatCString(aValue).get())) {
|
|
return NS_ERROR_INVALID_ARG;
|
|
}
|
|
|
|
*aIsWhitelisted = CrashReporter::IsAnnotationWhitelistedForPing(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(nsISupports* aOuter, REFNSIID aIID,
|
|
void** aResult) {
|
|
NS_ENSURE_NO_AGGREGATION(aOuter);
|
|
|
|
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;
|
|
}
|
|
}
|
|
|
|
// {5F5E59CE-27BC-47eb-9D1F-B09CA9049836}
|
|
static const nsCID kProfileServiceCID = {
|
|
0x5f5e59ce,
|
|
0x27bc,
|
|
0x47eb,
|
|
{0x9d, 0x1f, 0xb0, 0x9c, 0xa9, 0x4, 0x98, 0x36}};
|
|
|
|
static already_AddRefed<nsIFactory> ProfileServiceFactoryConstructor(
|
|
const mozilla::Module& module, const mozilla::Module::CIDEntry& entry) {
|
|
nsCOMPtr<nsIFactory> factory;
|
|
NS_NewToolkitProfileFactory(getter_AddRefs(factory));
|
|
return factory.forget();
|
|
}
|
|
|
|
static const mozilla::Module::CIDEntry kXRECIDs[] = {
|
|
{&kProfileServiceCID, false, ProfileServiceFactoryConstructor, nullptr},
|
|
{nullptr}};
|
|
|
|
static const mozilla::Module::ContractIDEntry kXREContracts[] = {
|
|
{NS_PROFILESERVICE_CONTRACTID, &kProfileServiceCID}, {nullptr}};
|
|
|
|
extern const mozilla::Module kXREModule = {mozilla::Module::kVersion, kXRECIDs,
|
|
kXREContracts};
|
|
|
|
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(nsISupports* aOuter, const nsIID& aIID,
|
|
void** aResult) {
|
|
NS_ENSURE_NO_AGGREGATION(aOuter);
|
|
|
|
return mSingleton->QueryInterface(aIID, aResult);
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsSingletonFactory::LockFactory(bool) { return NS_OK; }
|
|
|
|
/**
|
|
* 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
|
|
|
|
// 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) {
|
|
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) {
|
|
printf(", %s", (const char*)gAppData->copyright);
|
|
}
|
|
printf("\n");
|
|
}
|
|
|
|
static inline void DumpFullVersion() {
|
|
if (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) {
|
|
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) &&
|
|
Preferences::GetBool(kPrefDrawTabsInTitlebar, false);
|
|
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,
|
|
kPrefDrawTabsInTitlebar);
|
|
}
|
|
|
|
# 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::AnnotateCrashReport(
|
|
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 OnDefaultAgentRemoteSettingsPrefChanged(const char* aPref,
|
|
void* aData) {
|
|
nsresult rv;
|
|
nsAutoString valueName;
|
|
if (strcmp(aPref, kPrefServicesSettingsServer) == 0) {
|
|
valueName.AssignLiteral("ServicesSettingsServer");
|
|
} else if (strcmp(aPref, kPrefSecurityContentSignatureRootHash) == 0) {
|
|
valueName.AssignLiteral("SecurityContentSignatureRootHash");
|
|
} 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);
|
|
NS_ENSURE_SUCCESS_VOID(rv);
|
|
|
|
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;
|
|
}
|
|
|
|
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)
|
|
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;
|
|
}
|
|
return NS_ERROR_ABORT;
|
|
}
|
|
|
|
nsresult mRv;
|
|
};
|
|
|
|
} // namespace
|
|
|
|
static nsresult ProfileMissingDialog(nsINativeAppSupport* aNative) {
|
|
#ifdef MOZ_BACKGROUNDTASKS
|
|
if (BackgroundTasks::IsBackgroundTaskMode()) {
|
|
// We should never get to this point in background task mode.
|
|
Output(false,
|
|
"Could not determine any profile running in backgroundtask mode!\n");
|
|
return NS_ERROR_ABORT;
|
|
}
|
|
#endif
|
|
|
|
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;
|
|
}
|
|
}
|
|
|
|
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);
|
|
|
|
mozilla::Telemetry::WriteFailedProfileLock(aProfileDir);
|
|
|
|
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);
|
|
|
|
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
|
|
java::GeckoAppShell::KillAnyZombies();
|
|
button = 0;
|
|
#else
|
|
const uint32_t flags = (nsIPromptService::BUTTON_TITLE_IS_STRING *
|
|
nsIPromptService::BUTTON_POS_0) +
|
|
(nsIPromptService::BUTTON_TITLE_CANCEL *
|
|
nsIPromptService::BUTTON_POS_1);
|
|
|
|
bool checkState = false;
|
|
rv = ps->ConfirmEx(nullptr, killTitle.get(), killMessage.get(), flags,
|
|
killTitle.get(), nullptr, nullptr, nullptr,
|
|
&checkState, &button);
|
|
NS_ENSURE_SUCCESS_LOG(rv, rv);
|
|
#endif
|
|
|
|
if (button == 0) {
|
|
rv = aUnlocker->Unlock(nsIProfileUnlocker::FORCE_QUIT);
|
|
if (NS_FAILED(rv)) {
|
|
return rv;
|
|
}
|
|
|
|
SaveFileToEnv("XRE_PROFILE_PATH", aProfileDir);
|
|
SaveFileToEnv("XRE_PROFILE_LOCAL_PATH", aProfileLocalDir);
|
|
|
|
return LaunchChild(false, true);
|
|
}
|
|
} else {
|
|
#ifdef MOZ_WIDGET_ANDROID
|
|
if (java::GeckoAppShell::UnlockProfile()) {
|
|
return NS_LockProfilePath(aProfileDir, aProfileLocalDir, nullptr,
|
|
aResult);
|
|
}
|
|
#else
|
|
rv = ps->Alert(nullptr, killTitle.get(), killMessage.get());
|
|
NS_ENSURE_SUCCESS_LOG(rv, rv);
|
|
#endif
|
|
}
|
|
|
|
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
|
|
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);
|
|
|
|
nsCOMPtr<mozIDOMWindowProxy> newWindow;
|
|
rv = windowWatcher->OpenWindow(
|
|
nullptr, nsDependentCString(kProfileManagerURL), "_blank"_ns,
|
|
"centerscreen,chrome,modal,titlebar"_ns, 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;
|
|
}
|
|
|
|
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;
|
|
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");
|
|
}
|
|
|
|
// 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 : public JSONWriteFunc {
|
|
FILE* mFile;
|
|
explicit FileWriteFunc(FILE* aFile) : mFile(aFile) {}
|
|
|
|
void Write(const Span<const char>& aStr) override {
|
|
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;
|
|
nsCOMPtr<nsIUUIDGenerator> uuidGen =
|
|
do_GetService("@mozilla.org/uuid-generator;1");
|
|
NS_ENSURE_TRUE_VOID(uuidGen);
|
|
rv = uuidGen->GenerateUUIDInPlace(&uuid);
|
|
NS_ENSURE_SUCCESS_VOID(rv);
|
|
|
|
char strid[NSID_LENGTH];
|
|
uuid.ToProvidedString(strid);
|
|
|
|
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_LENGTH includes the trailing \0 and we also want to strip off the
|
|
// surrounding braces so the length becomes NSID_LENGTH - 3.
|
|
nsDependentCSubstring pingId(strid + 1, NSID_LENGTH - 3);
|
|
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);
|
|
|
|
nsCOMPtr<mozIDOMWindowProxy> newWindow;
|
|
rv = windowWatcher->OpenWindow(
|
|
nullptr, nsDependentCString(kProfileDowngradeURL), "_blank"_ns,
|
|
"centerscreen,chrome,modal,titlebar"_ns, 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;
|
|
|
|
#if defined(XP_UNIX) || defined(XP_BEOS)
|
|
# define PLATFORM_FASL_SUFFIX ".mfasl"
|
|
#elif defined(XP_WIN)
|
|
# define PLATFORM_FASL_SUFFIX ".mfl"
|
|
#endif
|
|
|
|
file->AppendNative(nsLiteralCString("XUL" PLATFORM_FASL_SUFFIX));
|
|
file->Remove(false);
|
|
|
|
file->SetNativeLeafName(nsLiteralCString("XPC" PLATFORM_FASL_SUFFIX));
|
|
file->Remove(false);
|
|
|
|
file->SetNativeLeafName("startupCache"_ns);
|
|
nsresult rv = file->Remove(true);
|
|
return NS_SUCCEEDED(rv) || rv == NS_ERROR_FILE_TARGET_DOES_NOT_EXIST ||
|
|
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
|
|
|
|
#ifdef USE_GLX_TEST
|
|
bool fire_glxtest_process();
|
|
#endif
|
|
|
|
#include "GeckoProfiler.h"
|
|
|
|
// Encapsulates startup and shutdown state for XRE_main
|
|
class XREMain {
|
|
public:
|
|
XREMain()
|
|
: mStartOffline(false),
|
|
mShuttingDown(false)
|
|
#ifdef MOZ_HAS_REMOTE
|
|
,
|
|
mDisableRemoteClient(false),
|
|
mDisableRemoteServer(false)
|
|
#endif
|
|
#if defined(MOZ_WIDGET_GTK)
|
|
,
|
|
mGdkDisplay(nullptr)
|
|
#endif
|
|
{};
|
|
|
|
~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();
|
|
|
|
Result<bool, nsresult> 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;
|
|
nsAutoCString mDesktopStartupID;
|
|
|
|
bool mStartOffline;
|
|
bool mShuttingDown;
|
|
#if defined(MOZ_HAS_REMOTE)
|
|
bool mDisableRemoteClient;
|
|
bool mDisableRemoteServer;
|
|
#endif
|
|
|
|
#if defined(MOZ_WIDGET_GTK)
|
|
GdkDisplay* mGdkDisplay;
|
|
#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
|
|
|
|
static void IncreaseDescriptorLimits() {
|
|
#ifdef XP_UNIX
|
|
// Increase the fd limit to accomodate IPC resources like shared memory.
|
|
// See also the Darwin case in config/external/nspr/pr/moz.build
|
|
static const rlim_t kFDs = 4096;
|
|
struct rlimit rlim;
|
|
|
|
if (getrlimit(RLIMIT_NOFILE, &rlim) != 0) {
|
|
Output(false, "getrlimit: %s\n", strerror(errno));
|
|
return;
|
|
}
|
|
// Don't decrease the limit if it's already high enough, but don't
|
|
// try to go over the hard limit. (RLIM_INFINITY isn't required to
|
|
// be the numerically largest rlim_t, so don't assume that.)
|
|
if (rlim.rlim_cur != RLIM_INFINITY && rlim.rlim_cur < kFDs &&
|
|
rlim.rlim_cur < rlim.rlim_max) {
|
|
if (rlim.rlim_max != RLIM_INFINITY && rlim.rlim_max < kFDs) {
|
|
rlim.rlim_cur = rlim.rlim_max;
|
|
} else {
|
|
rlim.rlim_cur = kFDs;
|
|
}
|
|
if (setrlimit(RLIMIT_NOFILE, &rlim) != 0) {
|
|
Output(false, "setrlimit: %s\n", strerror(errno));
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
/*
|
|
* 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)) {
|
|
backgroundTask = Some(backgroundTaskName);
|
|
}
|
|
BackgroundTasks::Init(backgroundTask);
|
|
|
|
if (BackgroundTasks::IsBackgroundTaskMode()) {
|
|
printf_stderr("*** You are running in background task mode. ***\n");
|
|
}
|
|
#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
|
|
}
|
|
|
|
nsresult rv;
|
|
ArgResult ar;
|
|
|
|
#ifdef DEBUG
|
|
if (PR_GetEnv("XRE_MAIN_BREAK")) NS_BREAK();
|
|
#endif
|
|
|
|
IncreaseDescriptorLimits();
|
|
|
|
#ifdef USE_GLX_TEST
|
|
// bug 639842 - it's very important to fire this process BEFORE we set up
|
|
// error handling. indeed, this process is expected to be crashy, and we
|
|
// don't want the user to see its crashes. That's the whole reason for
|
|
// doing this in a separate process.
|
|
//
|
|
// This call will cause a fork and the fork will terminate itself separately
|
|
// from the usual shutdown sequence
|
|
fire_glxtest_process();
|
|
#endif
|
|
|
|
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::AnnotateCrashReport(CrashReporter::Annotation::StartupCrash,
|
|
true);
|
|
|
|
// pass some basic info from the app data
|
|
if (mAppData->vendor)
|
|
CrashReporter::AnnotateCrashReport(CrashReporter::Annotation::Vendor,
|
|
nsDependentCString(mAppData->vendor));
|
|
if (mAppData->name)
|
|
CrashReporter::AnnotateCrashReport(CrashReporter::Annotation::ProductName,
|
|
nsDependentCString(mAppData->name));
|
|
if (mAppData->ID)
|
|
CrashReporter::AnnotateCrashReport(CrashReporter::Annotation::ProductID,
|
|
nsDependentCString(mAppData->ID));
|
|
if (mAppData->version)
|
|
CrashReporter::AnnotateCrashReport(CrashReporter::Annotation::Version,
|
|
nsDependentCString(mAppData->version));
|
|
if (mAppData->buildID)
|
|
CrashReporter::AnnotateCrashReport(CrashReporter::Annotation::BuildID,
|
|
nsDependentCString(mAppData->buildID));
|
|
|
|
nsDependentCString releaseChannel(MOZ_STRINGIFY(MOZ_UPDATE_CHANNEL));
|
|
CrashReporter::AnnotateCrashReport(
|
|
CrashReporter::Annotation::ReleaseChannel, releaseChannel);
|
|
#ifdef MOZ_LINKER
|
|
CrashReporter::AnnotateCrashReport(
|
|
CrashReporter::Annotation::CrashAddressLikelyWrong,
|
|
IsSignalHandlingBroken());
|
|
#endif
|
|
|
|
#ifdef XP_WIN
|
|
nsAutoString appInitDLLs;
|
|
if (widget::WinUtils::GetAppInitDLLs(appInitDLLs)) {
|
|
CrashReporter::AnnotateCrashReport(CrashReporter::Annotation::AppInitDLLs,
|
|
NS_ConvertUTF16toUTF8(appInitDLLs));
|
|
}
|
|
#endif
|
|
|
|
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);
|
|
}
|
|
}
|
|
}
|
|
|
|
#if defined(MOZ_SANDBOX) && defined(XP_WIN)
|
|
if (mAppData->sandboxBrokerServices) {
|
|
SandboxBroker::Initialize(mAppData->sandboxBrokerServices);
|
|
} 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.");
|
|
}
|
|
if (mAppData->sandboxPermissionsService) {
|
|
SandboxPermissions::Initialize(mAppData->sandboxPermissionsService,
|
|
nullptr);
|
|
}
|
|
#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();
|
|
|
|
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);
|
|
}
|
|
#endif
|
|
|
|
gSafeMode = safeModeRequested.value();
|
|
|
|
#ifdef XP_WIN
|
|
{
|
|
// Add CPU microcode version to the crash report as "CPUMicrocodeVersion".
|
|
// It feels like this code may belong in nsSystemInfo instead.
|
|
int cpuUpdateRevision = -1;
|
|
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 (size_t oneChoice = 0; oneChoice < ArrayLength(choices);
|
|
oneChoice++) {
|
|
if (RegQueryValueExW(key, choices[oneChoice], 0, &vtype,
|
|
reinterpret_cast<LPBYTE>(updateRevision),
|
|
&len) == ERROR_SUCCESS) {
|
|
if (vtype == REG_BINARY && len == sizeof(updateRevision)) {
|
|
// The first word is unused
|
|
cpuUpdateRevision = static_cast<int>(updateRevision[1]);
|
|
break;
|
|
} else if (vtype == REG_DWORD && len == sizeof(updateRevision[0])) {
|
|
cpuUpdateRevision = static_cast<int>(updateRevision[0]);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (cpuUpdateRevision > 0) {
|
|
CrashReporter::AnnotateCrashReport(
|
|
CrashReporter::Annotation::CPUMicrocodeVersion,
|
|
nsPrintfCString("0x%x", cpuUpdateRevision));
|
|
}
|
|
}
|
|
#endif
|
|
|
|
CrashReporter::AnnotateCrashReport(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;
|
|
if (!EnvHasValue("MOZ_NO_REMOTE")) {
|
|
SaveToEnv("MOZ_NO_REMOTE=1");
|
|
}
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
// Handle --help, --full-version and --version command line arguments.
|
|
// They should return quickly, so we deal with them here.
|
|
if (CheckArg("h") || CheckArg("help") || CheckArg("?")) {
|
|
DumpHelp();
|
|
*aExitFlag = true;
|
|
return 0;
|
|
}
|
|
|
|
if (CheckArg("v") || CheckArg("version")) {
|
|
DumpVersion();
|
|
*aExitFlag = true;
|
|
return 0;
|
|
}
|
|
|
|
if (CheckArg("full-version")) {
|
|
DumpFullVersion();
|
|
*aExitFlag = true;
|
|
return 0;
|
|
}
|
|
|
|
rv = XRE_InitCommandLine(gArgc, gArgv);
|
|
NS_ENSURE_SUCCESS(rv, 1);
|
|
|
|
return 0;
|
|
}
|
|
|
|
#ifdef XP_WIN
|
|
static bool QueryOneWMIProperty(IWbemServices* aServices,
|
|
const wchar_t* aWMIClass,
|
|
const wchar_t* aProperty, VARIANT* aResult) {
|
|
RefPtr<IEnumWbemClassObject> enumerator;
|
|
|
|
_bstr_t query(L"SELECT * FROM ");
|
|
query += _bstr_t(aWMIClass);
|
|
|
|
HRESULT hr = aServices->ExecQuery(
|
|
_bstr_t(L"WQL"), query,
|
|
WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY, nullptr,
|
|
getter_AddRefs(enumerator));
|
|
|
|
if (FAILED(hr) || !enumerator) {
|
|
return false;
|
|
}
|
|
|
|
RefPtr<IWbemClassObject> classObject;
|
|
ULONG results;
|
|
|
|
hr =
|
|
enumerator->Next(WBEM_INFINITE, 1, getter_AddRefs(classObject), &results);
|
|
|
|
if (FAILED(hr) || results == 0) {
|
|
return false;
|
|
}
|
|
|
|
hr = classObject->Get(aProperty, 0, aResult, 0, 0);
|
|
|
|
return SUCCEEDED(hr);
|
|
}
|
|
|
|
/**
|
|
* Uses WMI to read some information that may be useful for diagnosing
|
|
* crashes. This function is best-effort; failures shouldn't burden the
|
|
* caller. COM must be initialized before calling.
|
|
*/
|
|
|
|
static const char kMemoryErrorCorrectionValues[][15] = {
|
|
"Reserved", // 0
|
|
"Other", // 1
|
|
"Unknown", // 2
|
|
"None", // 3
|
|
"Parity", // 4
|
|
"Single-bit ECC", // 5
|
|
"Multi-bit ECC", // 6
|
|
"CRC" // 7
|
|
};
|
|
|
|
static void AnnotateWMIData() {
|
|
RefPtr<IWbemLocator> locator;
|
|
|
|
HRESULT hr =
|
|
CoCreateInstance(CLSID_WbemLocator, nullptr, CLSCTX_INPROC_SERVER,
|
|
IID_IWbemLocator, getter_AddRefs(locator));
|
|
|
|
if (FAILED(hr)) {
|
|
return;
|
|
}
|
|
|
|
RefPtr<IWbemServices> services;
|
|
|
|
hr =
|
|
locator->ConnectServer(_bstr_t(L"ROOT\\CIMV2"), nullptr, nullptr, nullptr,
|
|
0, nullptr, nullptr, getter_AddRefs(services));
|
|
|
|
if (FAILED(hr)) {
|
|
return;
|
|
}
|
|
|
|
hr = CoSetProxyBlanket(services, RPC_C_AUTHN_WINNT, RPC_C_AUTHZ_NONE, nullptr,
|
|
RPC_C_AUTHN_LEVEL_CALL, RPC_C_IMP_LEVEL_IMPERSONATE,
|
|
nullptr, EOAC_NONE);
|
|
|
|
if (FAILED(hr)) {
|
|
return;
|
|
}
|
|
|
|
VARIANT value;
|
|
VariantInit(&value);
|
|
|
|
// Annotate information about the system manufacturer.
|
|
if (QueryOneWMIProperty(services, L"Win32_BIOS", L"Manufacturer", &value) &&
|
|
V_VT(&value) == VT_BSTR) {
|
|
CrashReporter::AnnotateCrashReport(
|
|
CrashReporter::Annotation::BIOS_Manufacturer,
|
|
NS_ConvertUTF16toUTF8(V_BSTR(&value)));
|
|
}
|
|
|
|
VariantClear(&value);
|
|
|
|
// Annotate information about type of memory error correction.
|
|
if (QueryOneWMIProperty(services, L"Win32_PhysicalMemoryArray",
|
|
L"MemoryErrorCorrection", &value) &&
|
|
V_VT(&value) == VT_I4) {
|
|
long valueInt = V_I4(&value);
|
|
nsCString valueString;
|
|
if (valueInt < 0 ||
|
|
valueInt >= long(ArrayLength(kMemoryErrorCorrectionValues))) {
|
|
valueString.AssignLiteral("Unexpected value");
|
|
} else {
|
|
valueString.AssignASCII(kMemoryErrorCorrectionValues[valueInt]);
|
|
}
|
|
CrashReporter::AnnotateCrashReport(
|
|
CrashReporter::Annotation::MemoryErrorCorrection, valueString);
|
|
}
|
|
|
|
VariantClear(&value);
|
|
}
|
|
|
|
static void PR_CALLBACK AnnotateWMIData_ThreadStart(void*) {
|
|
mscom::MTARegion mta;
|
|
if (!mta.IsValid()) {
|
|
return;
|
|
}
|
|
|
|
AnnotateWMIData();
|
|
}
|
|
#endif // XP_WIN
|
|
|
|
#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_WAYLAND)
|
|
bool IsWaylandEnabled() {
|
|
const char* waylandDisplay = PR_GetEnv("WAYLAND_DISPLAY");
|
|
if (!waylandDisplay) {
|
|
return false;
|
|
}
|
|
|
|
// MOZ_ENABLE_WAYLAND is our primary Wayland on/off switch.
|
|
const char* waylandPref = PR_GetEnv("MOZ_ENABLE_WAYLAND");
|
|
bool enableWayland = (waylandPref && *waylandPref);
|
|
if (!enableWayland) {
|
|
const char* backendPref = PR_GetEnv("GDK_BACKEND");
|
|
enableWayland = (backendPref && strncmp(backendPref, "wayland", 7) == 0);
|
|
if (enableWayland) {
|
|
NS_WARNING(
|
|
"Wayland backend should be enabled by MOZ_ENABLE_WAYLAND=1."
|
|
"GDK_BACKEND is a Gtk3 debug variable and may cause various issues.");
|
|
}
|
|
}
|
|
if (enableWayland && gtk_check_version(3, 22, 0) != nullptr) {
|
|
NS_WARNING("Running Wayland backen on Gtk3 < 3.22. Expect issues/glitches");
|
|
}
|
|
return enableWayland;
|
|
}
|
|
#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 within the
|
|
// last 6 hours.
|
|
// Note that this duplicates the logic in nsAppStartup::TrackStartupCrashBegin,
|
|
// which runs too late for our purposes.
|
|
Result<bool, nsresult> XREMain::CheckLastStartupWasCrash() {
|
|
constexpr int32_t MAX_TIME_SINCE_STARTUP = 6 * 60 * 60 * 1000;
|
|
|
|
nsCOMPtr<nsIFile> crashFile;
|
|
MOZ_TRY_VAR(crashFile, GetIncompleteStartupFile(mProfLD));
|
|
|
|
// Attempt to create the incomplete startup canary file. If the file already
|
|
// exists, this fails, and we know the last startup was a success. 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->OpenNSPRFileDesc(PR_WRONLY | PR_CREATE_FILE | PR_EXCL,
|
|
0666, &fd.rwget());
|
|
if (fd) {
|
|
return false;
|
|
}
|
|
|
|
PRTime lastModifiedTime;
|
|
MOZ_TRY(crashFile->GetLastModifiedTime(&lastModifiedTime));
|
|
|
|
// If the file exists, and was created within the appropriate time window,
|
|
// the last startup was recent and resulted in a crash.
|
|
PRTime now = PR_Now() / PR_USEC_PER_MSEC;
|
|
return now - lastModifiedTime <= MAX_TIME_SINCE_STARTUP;
|
|
}
|
|
|
|
/*
|
|
* 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.
|
|
#ifndef FUZZING
|
|
# 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 /* 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 */
|
|
|
|
#if defined(MOZ_WIDGET_GTK)
|
|
// Stash DESKTOP_STARTUP_ID in malloc'ed memory because gtk_init will clear
|
|
// it.
|
|
# define HAVE_DESKTOP_STARTUP_ID
|
|
const char* desktopStartupIDEnv = PR_GetEnv("DESKTOP_STARTUP_ID");
|
|
if (desktopStartupIDEnv) {
|
|
mDesktopStartupID.Assign(desktopStartupIDEnv);
|
|
}
|
|
#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.
|
|
{
|
|
nsAutoCString program(gAppData->name);
|
|
ToLowerCase(program);
|
|
g_set_prgname(program.get());
|
|
}
|
|
|
|
// 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;
|
|
}
|
|
|
|
#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 (!gfxPlatform::IsHeadless()) {
|
|
XInitThreads();
|
|
}
|
|
#endif
|
|
#if defined(MOZ_WIDGET_GTK)
|
|
if (!gfxPlatform::IsHeadless()) {
|
|
const char* display_name = nullptr;
|
|
bool saveDisplayArg = false;
|
|
|
|
// display_name is owned by gdk.
|
|
display_name = gdk_get_display_arg_name();
|
|
// 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;
|
|
}
|
|
|
|
bool waylandEnabled = false;
|
|
# if defined(MOZ_WAYLAND)
|
|
waylandEnabled = IsWaylandEnabled();
|
|
# endif
|
|
// 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) {
|
|
mGdkDisplay = gdk_display_open(display_name);
|
|
if (!mGdkDisplay) {
|
|
PR_fprintf(PR_STDERR, "Error: cannot open display: %s\n", display_name);
|
|
return 1;
|
|
}
|
|
gdk_display_manager_set_default_display(gdk_display_manager_get(),
|
|
mGdkDisplay);
|
|
if (saveDisplayArg) {
|
|
if (GDK_IS_X11_DISPLAY(mGdkDisplay)) {
|
|
SaveWordToEnv("DISPLAY", nsDependentCString(display_name));
|
|
}
|
|
# ifdef MOZ_WAYLAND
|
|
else if (!GDK_IS_X11_DISPLAY(mGdkDisplay)) {
|
|
SaveWordToEnv("WAYLAND_DISPLAY", nsDependentCString(display_name));
|
|
}
|
|
# endif
|
|
}
|
|
}
|
|
# ifdef MOZ_WIDGET_GTK
|
|
else {
|
|
mGdkDisplay =
|
|
gdk_display_manager_open_display(gdk_display_manager_get(), nullptr);
|
|
}
|
|
# 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();
|
|
}
|
|
#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
|
|
|
|
rv = NS_CreateNativeAppSupport(getter_AddRefs(mNativeApp));
|
|
if (NS_FAILED(rv)) return 1;
|
|
|
|
bool canRun = false;
|
|
rv = mNativeApp->Start(&canRun);
|
|
if (NS_FAILED(rv) || !canRun) {
|
|
return 1;
|
|
}
|
|
|
|
#if defined(HAVE_DESKTOP_STARTUP_ID) && defined(MOZ_WIDGET_GTK)
|
|
// DESKTOP_STARTUP_ID is cleared now,
|
|
// we recover it in case we need a restart.
|
|
if (!mDesktopStartupID.IsEmpty()) {
|
|
nsAutoCString desktopStartupEnv;
|
|
desktopStartupEnv.AssignLiteral("DESKTOP_STARTUP_ID=");
|
|
desktopStartupEnv.Append(mDesktopStartupID);
|
|
// Leak it with extreme prejudice!
|
|
PR_SetEnv(ToNewCString(desktopStartupEnv));
|
|
}
|
|
#endif
|
|
|
|
// Support exiting early for testing startup sequence. Bug 1360493
|
|
if (CheckArg("test-launch-without-hang")) {
|
|
*aExitFlag = true;
|
|
return 0;
|
|
}
|
|
|
|
rv = NS_NewToolkitProfileService(getter_AddRefs(mProfileSvc));
|
|
if (rv == NS_ERROR_FILE_ACCESS_DENIED) {
|
|
PR_fprintf(PR_STDERR,
|
|
"Error: Access was denied while trying to open files in "
|
|
"your profile directory.\n");
|
|
}
|
|
if (NS_FAILED(rv)) {
|
|
// We failed to choose or create profile - notify user and quit
|
|
ProfileMissingDialog(mNativeApp);
|
|
return 1;
|
|
}
|
|
|
|
#ifdef MOZ_BACKGROUNDTASKS
|
|
if (BackgroundTasks::IsBackgroundTaskMode()) {
|
|
nsCOMPtr<nsIFile> file;
|
|
nsresult rv = BackgroundTasks::GetOrCreateTemporaryProfileDirectory(
|
|
getter_AddRefs(file));
|
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
|
return 1;
|
|
}
|
|
|
|
SaveFileToEnv("XRE_PROFILE_PATH", file);
|
|
}
|
|
#endif
|
|
|
|
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.
|
|
const char* desktopStartupIDPtr =
|
|
mDesktopStartupID.IsEmpty() ? nullptr : mDesktopStartupID.get();
|
|
|
|
RemoteResult rr = mRemoteService->StartClient(desktopStartupIDPtr);
|
|
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)
|
|
// 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;
|
|
}
|
|
#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) {
|
|
// 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")) {
|
|
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().unwrapOr(false);
|
|
|
|
if (CheckArg("purgecaches") || PR_GetEnv("MOZ_PURGE_CACHES") ||
|
|
lastStartupWasCrash || gSafeMode) {
|
|
cachesOK = false;
|
|
}
|
|
|
|
// 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);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
#if defined(MOZ_SANDBOX)
|
|
void AddSandboxAnnotations() {
|
|
// Include the sandbox content level, regardless of platform
|
|
int level = GetEffectiveContentSandboxLevel();
|
|
|
|
nsAutoCString levelString;
|
|
levelString.AppendInt(level);
|
|
|
|
CrashReporter::AnnotateCrashReport(
|
|
CrashReporter::Annotation::ContentSandboxLevel, levelString);
|
|
|
|
// Include whether or not this instance is capable of content sandboxing
|
|
bool sandboxCapable = false;
|
|
|
|
# if defined(XP_WIN)
|
|
// All supported Windows versions support some level of content sandboxing
|
|
sandboxCapable = true;
|
|
# elif defined(XP_MACOSX)
|
|
// All supported OS X versions are capable
|
|
sandboxCapable = true;
|
|
# elif defined(XP_LINUX)
|
|
sandboxCapable = SandboxInfo::Get().CanSandboxContent();
|
|
# elif defined(__OpenBSD__)
|
|
sandboxCapable = true;
|
|
StartOpenBSDSandbox(GeckoProcessType_Default);
|
|
# endif
|
|
|
|
CrashReporter::AnnotateCrashReport(
|
|
CrashReporter::Annotation::ContentSandboxCapable, sandboxCapable);
|
|
}
|
|
#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();
|
|
auto dllServicesDisable =
|
|
MakeScopeExit([&dllServices]() { dllServices->DisableFull(); });
|
|
|
|
# if defined(MOZ_GECKO_PROFILER)
|
|
mozilla::mscom::InitProfilerMarkers();
|
|
# endif // defined(MOZ_GECKO_PROFILER)
|
|
#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::AnnotateCrashReport(
|
|
CrashReporter::Annotation::ReleaseChannel, sval);
|
|
}
|
|
}
|
|
}
|
|
// Needs to be set after xpcom initialization.
|
|
CrashReporter::AnnotateCrashReport(
|
|
CrashReporter::Annotation::FramePoisonBase,
|
|
nsPrintfCString("%.16" PRIu64, uint64_t(gMozillaPoisonBase)));
|
|
CrashReporter::AnnotateCrashReport(
|
|
CrashReporter::Annotation::FramePoisonSize,
|
|
uint32_t(gMozillaPoisonSize));
|
|
|
|
bool includeContextHeap = Preferences::GetBool(
|
|
"toolkit.crashreporter.include_context_heap", false);
|
|
CrashReporter::SetIncludeContextHeap(includeContextHeap);
|
|
|
|
#ifdef XP_WIN
|
|
PR_CreateThread(PR_USER_THREAD, AnnotateWMIData_ThreadStart, 0,
|
|
PR_PRIORITY_LOW, PR_GLOBAL_THREAD, PR_UNJOINABLE_THREAD, 0);
|
|
#endif
|
|
|
|
#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);
|
|
}
|
|
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");
|
|
}
|
|
}
|
|
}
|
|
|
|
#ifndef XP_WIN
|
|
nsCOMPtr<nsIFile> profileDir;
|
|
nsAutoCString path;
|
|
rv = mDirProvider.GetProfileStartupDir(getter_AddRefs(profileDir));
|
|
if (NS_SUCCEEDED(rv) && NS_SUCCEEDED(profileDir->GetNativePath(path)) &&
|
|
!IsUtf8(path)) {
|
|
PR_fprintf(
|
|
PR_STDERR,
|
|
"Error: The profile path is not valid UTF-8. Unable to continue.\n");
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
#endif
|
|
|
|
// 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();
|
|
|
|
nsAppStartupNotifier::NotifyObservers(APPSTARTUP_CATEGORY);
|
|
|
|
appStartup = components::AppStartup::Service();
|
|
NS_ENSURE_TRUE(appStartup, NS_ERROR_FAILURE);
|
|
|
|
mDirProvider.DoStartup();
|
|
|
|
// As FilePreferences need the profile directory, we must initialize right
|
|
// here.
|
|
mozilla::FilePreferences::InitDirectoriesWhitelist();
|
|
mozilla::FilePreferences::InitPrefs();
|
|
|
|
OverrideDefaultLocaleIfNeeded();
|
|
|
|
nsCString userAgentLocale;
|
|
LocaleService::GetInstance()->GetAppLocaleAsBCP47(userAgentLocale);
|
|
CrashReporter::AnnotateCrashReport(
|
|
CrashReporter::Annotation::useragent_locale, userAgentLocale);
|
|
|
|
appStartup->GetShuttingDown(&mShuttingDown);
|
|
|
|
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;
|
|
}
|
|
|
|
if (!mShuttingDown) {
|
|
cmdLine = new nsCommandLine();
|
|
|
|
rv = cmdLine->Init(gArgc, gArgv, workingDir,
|
|
nsICommandLine::STATE_INITIAL_LAUNCH);
|
|
NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
|
|
|
|
/* 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 (!mShuttingDown) {
|
|
#ifdef XP_MACOSX
|
|
bool lazyHiddenWindow = false;
|
|
#else
|
|
bool lazyHiddenWindow =
|
|
Preferences::GetBool("toolkit.lazyHiddenWindow", false);
|
|
#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(
|
|
&OnDefaultAgentRemoteSettingsPrefChanged,
|
|
kPrefSecurityContentSignatureRootHash);
|
|
SetDefaultAgentLastRunTime();
|
|
}
|
|
# endif // defined(MOZ_DEFAULT_BROWSER_AGENT)
|
|
#endif
|
|
|
|
#if defined(HAVE_DESKTOP_STARTUP_ID) && defined(MOZ_WIDGET_GTK)
|
|
// Clear the environment variable so it won't be inherited by
|
|
// child processes and confuse things.
|
|
g_unsetenv("DESKTOP_STARTUP_ID");
|
|
#endif
|
|
|
|
#ifdef XP_MACOSX
|
|
// 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);
|
|
#endif
|
|
|
|
nsCOMPtr<nsIObserverService> obsService =
|
|
mozilla::services::GetObserverService();
|
|
if (obsService)
|
|
obsService->NotifyObservers(nullptr, "final-ui-startup", nullptr);
|
|
|
|
(void)appStartup->DoneStartingUp();
|
|
|
|
CrashReporter::AnnotateCrashReport(
|
|
CrashReporter::Annotation::StartupCrash, false);
|
|
|
|
appStartup->GetShuttingDown(&mShuttingDown);
|
|
}
|
|
|
|
if (!mShuttingDown) {
|
|
rv = cmdLine->Run();
|
|
NS_ENSURE_SUCCESS_LOG(rv, NS_ERROR_FAILURE);
|
|
|
|
appStartup->GetShuttingDown(&mShuttingDown);
|
|
}
|
|
|
|
if (!mShuttingDown) {
|
|
#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();
|
|
}
|
|
#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
|
|
Telemetry::ScalarSet(Telemetry::ScalarID::GECKO_VERSION,
|
|
NS_ConvertASCIItoUTF16(gAppData->version));
|
|
Telemetry::ScalarSet(Telemetry::ScalarID::GECKO_BUILD_ID,
|
|
NS_ConvertASCIItoUTF16(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));
|
|
nsAutoCString flagsString;
|
|
flagsString.AppendInt(sandboxInfo.AsInteger());
|
|
|
|
CrashReporter::AnnotateCrashReport(
|
|
CrashReporter::Annotation::ContentSandboxCapabilities, flagsString);
|
|
#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() {
|
|
nsresult rv;
|
|
|
|
const char* path = nullptr;
|
|
ArgResult ar = CheckArg("greomni", &path);
|
|
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 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));
|
|
}
|
|
|
|
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();
|
|
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) && defined(MOZ_SANDBOX)
|
|
mAppData->sandboxBrokerServices = aConfig.sandboxBrokerServices;
|
|
mAppData->sandboxPermissionsService = aConfig.sandboxPermissionsService;
|
|
#endif
|
|
|
|
mozilla::IOInterposerInit ioInterposerGuard;
|
|
|
|
#if defined(XP_WIN)
|
|
// Initializing COM below may load modules via SetWindowHookEx, some of
|
|
// which may modify the executable's IAT for ntdll.dll. If that happens,
|
|
// this browser process fails to launch sandbox processes because we cannot
|
|
// copy a modified IAT into a remote process (See SandboxBroker::LaunchApp).
|
|
// To prevent that, we cache the intact IAT before COM initialization.
|
|
// If EAF+ is enabled, CacheNtDllThunk() causes a crash, but EAF+ will
|
|
// also prevent an injected module from parsing the PE headers and modifying
|
|
// the IAT. Therefore, we can skip CacheNtDllThunk().
|
|
if (!mozilla::IsEafPlusEnabled()) {
|
|
mozilla::ipc::GeckoChildProcessHost::CacheNtDllThunk();
|
|
}
|
|
|
|
// Some COM settings are global to the process and must be set before any non-
|
|
// trivial COM is run in the application. Since these settings may affect
|
|
// stability, we should instantiate COM ASAP so that we can ensure that these
|
|
// global settings are configured before anything can interfere.
|
|
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>();
|
|
if (!mScopedXPCOM) return 1;
|
|
|
|
rv = mScopedXPCOM->Initialize(/* aInitJSContext = */ false);
|
|
NS_ENSURE_SUCCESS(rv, 1);
|
|
|
|
// run!
|
|
rv = XRE_mainRun();
|
|
|
|
#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;
|
|
|
|
#if defined(XP_WIN)
|
|
mozilla::widget::StopAudioSession();
|
|
#endif
|
|
|
|
// unlock the profile after ScopedXPCOMStartup object (xpcom)
|
|
// has gone out of scope. see bug #386739 for more details
|
|
mProfileLock->Unlock();
|
|
gProfileLock = nullptr;
|
|
|
|
#ifdef MOZ_BACKGROUNDTASKS
|
|
BackgroundTasks::Shutdown();
|
|
#endif
|
|
|
|
gLastAppVersion.Truncate();
|
|
gLastAppBuildID.Truncate();
|
|
|
|
mozilla::AppShutdown::MaybeDoRestart();
|
|
|
|
#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();
|
|
# endif
|
|
}
|
|
#endif
|
|
|
|
if (mAppData->flags & NS_XRE_ENABLE_CRASH_REPORTER)
|
|
CrashReporter::UnsetExceptionHandler();
|
|
|
|
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();
|
|
return result;
|
|
}
|
|
|
|
nsresult XRE_InitCommandLine(int aArgc, char* aArgv[]) {
|
|
nsresult rv = NS_OK;
|
|
|
|
#if defined(OS_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)
|
|
nsCOMPtr<nsIFile> greOmni = gAppData ? gAppData->xreDirectory : GreOmniPath();
|
|
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 mozilla::startup::sChildProcessType;
|
|
}
|
|
|
|
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_name, string_name, xre_name, bin_type) \
|
|
bool XRE_Is##xre_name##Process() { \
|
|
return XRE_GetProcessType() == GeckoProcessType_##enum_name; \
|
|
}
|
|
#include "mozilla/GeckoProcessTypes.h"
|
|
#undef GECKO_PROCESS_TYPE
|
|
|
|
bool XRE_UseNativeEventProcessing() {
|
|
#if defined(XP_MACOSX) || defined(XP_WIN)
|
|
if (XRE_IsRDDProcess() || XRE_IsSocketProcess()) {
|
|
return false;
|
|
}
|
|
#endif
|
|
if (XRE_IsContentProcess()) {
|
|
return StaticPrefs::dom_ipc_useNativeEventProcessing_content();
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
// Note: This function should not be needed anymore. See Bug 818634 for details.
|
|
void OverrideDefaultLocaleIfNeeded() {
|
|
// Read pref to decide whether to override default locale with US English.
|
|
if (mozilla::Preferences::GetBool("javascript.use_us_english_locale",
|
|
false)) {
|
|
// Set the application-wide C-locale. Needed to resist fingerprinting
|
|
// of Date.toLocaleFormat(). We use the locale to "C.UTF-8" if possible,
|
|
// to avoid interfering with non-ASCII keyboard input on some Linux
|
|
// desktops. Otherwise fall back to the "C" locale, which is available on
|
|
// all platforms.
|
|
setlocale(LC_ALL, "C.UTF-8") || setlocale(LC_ALL, "C");
|
|
}
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
switch (aProcessType) {
|
|
#define GECKO_PROCESS_TYPE(enum_name, string_name, xre_name, bin_type) \
|
|
case GeckoProcessType_##enum_name: \
|
|
return BinPathType::bin_type;
|
|
#include "mozilla/GeckoProcessTypes.h"
|
|
#undef GECKO_PROCESS_TYPE
|
|
default:
|
|
return BinPathType::PluginContainer;
|
|
}
|
|
}
|
|
|
|
// Because rust doesn't handle weak symbols, this function wraps the weak
|
|
// malloc_handle_oom for it.
|
|
extern "C" void GeckoHandleOOM(size_t size) { mozalloc_handle_oom(size); }
|
|
|
|
// From toolkit/library/rust/shared/lib.rs
|
|
extern "C" void install_rust_panic_hook();
|
|
extern "C" void install_rust_oom_hook();
|
|
|
|
struct InstallRustHooks {
|
|
InstallRustHooks() {
|
|
install_rust_panic_hook();
|
|
install_rust_oom_hook();
|
|
}
|
|
};
|
|
|
|
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
|