diff -r 000000000000 -r 6474c204b198 toolkit/xre/nsAppRunner.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/toolkit/xre/nsAppRunner.cpp Wed Dec 31 06:09:35 2014 +0100 @@ -0,0 +1,4488 @@ +/* -*- 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/. */ + +#if defined(MOZ_WIDGET_QT) +#include +#include +#include "nsQAppInstance.h" +#endif // MOZ_WIDGET_QT + +#include "mozilla/dom/ContentParent.h" +#include "mozilla/dom/ContentChild.h" + +#include "mozilla/ArrayUtils.h" +#include "mozilla/Attributes.h" +#include "mozilla/IOInterposer.h" +#include "mozilla/Likely.h" +#include "mozilla/Poison.h" +#include "mozilla/Preferences.h" +#include "mozilla/Telemetry.h" + +#include "nsAppRunner.h" +#include "mozilla/AppData.h" +#include "nsUpdateDriver.h" +#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 +#include +#endif + +#include "prmem.h" +#include "prnetdb.h" +#include "prprf.h" +#include "prproces.h" +#include "prenv.h" + +#include "nsIAppShellService.h" +#include "nsIAppStartup.h" +#include "nsIAppStartupNotifier.h" +#include "nsIMutableArray.h" +#include "nsICategoryManager.h" +#include "nsIChromeRegistry.h" +#include "nsICommandLineRunner.h" +#include "nsIComponentManager.h" +#include "nsIComponentRegistrar.h" +#include "nsIContentHandler.h" +#include "nsIDialogParamBlock.h" +#include "nsIDOMWindow.h" +#include "mozilla/ModuleUtils.h" +#include "nsIIOService2.h" +#include "nsIObserverService.h" +#include "nsINativeAppSupport.h" +#include "nsIProcess.h" +#include "nsIProfileUnlocker.h" +#include "nsIPromptService.h" +#include "nsIServiceManager.h" +#include "nsIStringBundle.h" +#include "nsISupportsPrimitives.h" +#include "nsIToolkitChromeRegistry.h" +#include "nsIToolkitProfile.h" +#include "nsIToolkitProfileService.h" +#include "nsIURI.h" +#include "nsIWindowCreator.h" +#include "nsIWindowMediator.h" +#include "nsIWindowWatcher.h" +#include "nsIXULAppInfo.h" +#include "nsIXULRuntime.h" +#include "nsPIDOMWindow.h" +#include "nsIBaseWindow.h" +#include "nsIWidget.h" +#include "nsIDocShell.h" +#include "nsAppShellCID.h" +#include "mozilla/scache/StartupCache.h" + +#include "mozilla/unused.h" + +#ifdef XP_WIN +#include "nsIWinAppHelper.h" +#include +#include "cairo/cairo-features.h" +#include "mozilla/WindowsVersion.h" +#ifdef MOZ_METRO +#include +#endif + +#ifndef PROCESS_DEP_ENABLE +#define PROCESS_DEP_ENABLE 0x1 +#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 "nsXPIDLString.h" +#include "nsPrintfCString.h" +#include "nsVersionComparator.h" + +#include "nsAppDirectoryServiceDefs.h" +#include "nsXULAppAPI.h" +#include "nsXREDirProvider.h" +#include "nsToolkitCompsCID.h" + +#if defined(XP_WIN) && defined(MOZ_METRO) +#include "updatehelper.h" +#endif + +#include "nsINIParser.h" +#include "mozilla/Omnijar.h" +#include "mozilla/StartupTimeline.h" +#include "mozilla/LateWriteChecks.h" + +#include + +#ifdef XP_UNIX +#include +#include +#include +#endif + +#ifdef XP_WIN +#include +#include +#include "nsThreadUtils.h" +#include +#include +#endif + +#ifdef XP_MACOSX +#include "nsILocalFileMac.h" +#include "nsCommandLineServiceMac.h" +#endif + +// for X remote support +#ifdef MOZ_ENABLE_XREMOTE +#include "XRemoteClient.h" +#include "nsIRemoteService.h" +#endif + +#ifdef NS_TRACE_MALLOC +#include "nsTraceMalloc.h" +#endif + +#if defined(DEBUG) && defined(XP_WIN32) +#include +#endif + +#if defined (XP_MACOSX) +#include +#endif + +#ifdef DEBUG +#include "prlog.h" +#endif + +#ifdef MOZ_JPROF +#include "jprof.h" +#endif + +#ifdef MOZ_CRASHREPORTER +#include "nsExceptionHandler.h" +#include "nsICrashReporter.h" +#define NS_CRASHREPORTER_CONTRACTID "@mozilla.org/toolkit/crash-reporter;1" +#include "nsIPrefService.h" +#endif + +#include "base/command_line.h" +#ifdef MOZ_ENABLE_TESTS +#include "GTestRunner.h" +#endif + +#ifdef MOZ_WIDGET_ANDROID +#include "AndroidBridge.h" +#endif + +extern uint32_t gRestartMode; +extern void InstallSignalHandlers(const char *ProgramName); +#include "nsX11ErrorHandler.h" + +#define FILE_COMPATIBILITY_INFO NS_LITERAL_CSTRING("compatibility.ini") +#define FILE_INVALIDATE_CACHES NS_LITERAL_CSTRING(".purgecaches") + +int gArgc; +char **gArgv; + +static const char gToolkitVersion[] = NS_STRINGIFY(GRE_MILESTONE); +static const char gToolkitBuildID[] = NS_STRINGIFY(GRE_BUILDID); + +static nsIProfileLock* gProfileLock; + +int gRestartArgc; +char **gRestartArgv; + +#ifdef MOZ_WIDGET_QT +static int gQtOnlyArgc; +static char **gQtOnlyArgv; +#endif + +#if defined(MOZ_WIDGET_GTK) +#if defined(DEBUG) || defined(NS_BUILD_REFCNT_LOGGING) \ + || defined(NS_TRACE_MALLOC) +#define CLEANUP_MEMORY 1 +#define PANGO_ENABLE_BACKEND +#include +#endif +#include +#ifdef MOZ_X11 +#include +#endif /* MOZ_X11 */ +#include "nsGTKToolkit.h" +#include +#endif +#include "BinaryPath.h" + +#ifdef MOZ_LINKER +extern "C" MFBT_API bool IsSignalHandlingBroken(); +#endif + +namespace mozilla { +int (*RunGTest)() = 0; +} + +using namespace mozilla; +using mozilla::unused; +using mozilla::scache::StartupCache; +using mozilla::dom::ContentParent; +using mozilla::dom::ContentChild; + +// Save literal putenv string to environment variable. +static void +SaveToEnv(const char *putenv) +{ + char *expr = strdup(putenv); + if (expr) + PR_SetEnv(expr); + // We intentionally leak |expr| here since it is required by PR_SetEnv. +} + +// Tests that an environment variable exists and has a value +static bool +EnvHasValue(const char *name) +{ + const char *val = PR_GetEnv(name); + return (val && *val); +} + +// Save the given word to the specified environment variable. +static void +SaveWordToEnv(const char *name, const nsACString & word) +{ + char *expr = PR_smprintf("%s=%s", name, PromiseFlatCString(word).get()); + 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 +} + +// Load the path of a file saved with SaveFileToEnv +static already_AddRefed +GetFileFromEnv(const char *name) +{ + nsresult rv; + nsCOMPtr file; + +#ifdef XP_WIN + WCHAR path[_MAX_PATH]; + if (!GetEnvironmentVariableW(NS_ConvertASCIItoUTF16(name).get(), + path, _MAX_PATH)) + return nullptr; + + rv = NS_NewLocalFile(nsDependentString(path), true, getter_AddRefs(file)); + if (NS_FAILED(rv)) + return nullptr; + + return file.forget(); +#else + const char *arg = PR_GetEnv(name); + if (!arg || !*arg) + return nullptr; + + rv = NS_NewNativeLocalFile(nsDependentCString(arg), true, + getter_AddRefs(file)); + if (NS_FAILED(rv)) + return nullptr; + + return file.forget(); +#endif +} + +// Save the path of the given word to the specified environment variable +// provided the environment variable does not have a value. +static void +SaveWordToEnvIfUnset(const char *name, const nsACString & word) +{ + if (!EnvHasValue(name)) + SaveWordToEnv(name, word); +} + +// Save the path of the given file to the specified environment variable +// provided the environment variable does not have a value. +static void +SaveFileToEnvIfUnset(const char *name, nsIFile *file) +{ + if (!EnvHasValue(name)) + SaveFileToEnv(name, file); +} + +static bool +strimatch(const char* lowerstr, const char* mixedstr) +{ + while(*lowerstr) { + if (!*mixedstr) return false; // mixedstr is shorter + if (tolower(*mixedstr) != *lowerstr) return false; // no match + + ++lowerstr; + ++mixedstr; + } + + if (*mixedstr) return false; // lowerstr is shorter + + return true; +} + +/** + * 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 void Output(bool isError, const char *fmt, ... ) +{ + va_list ap; + va_start(ap, fmt); + +#if defined(XP_WIN) && !MOZ_WINCONSOLE + char *msg = PR_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, + -1, + wide_msg, + sizeof(wide_msg) / sizeof(wchar_t)); + + MessageBoxW(nullptr, wide_msg, L"XULRunner", flags); + PR_smprintf_free(msg); + } +#else + vfprintf(stderr, fmt, ap); +#endif + + va_end(ap); +} + +enum RemoteResult { + REMOTE_NOT_FOUND = 0, + REMOTE_FOUND = 1, + REMOTE_ARG_BAD = 2 +}; + +enum ArgResult { + ARG_NONE = 0, + ARG_FOUND = 1, + ARG_BAD = 2 // you wanted a param, but there isn't one +}; + +static void RemoveArg(char **argv) +{ + do { + *argv = *(argv + 1); + ++argv; + } while (*argv); + + --gArgc; +} + +/** + * 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 aCheckOSInt if true returns ARG_BAD if the osint argument is present + * when aArg is also present. + * @param aParam if non-null, the -arg will be stored in this pointer. + * This is *not* allocated, but rather a pointer to the argv data. + * @param aRemArg if true, the argument is removed from the gArgv array. + */ +static ArgResult +CheckArg(const char* aArg, bool aCheckOSInt = false, const char **aParam = nullptr, bool aRemArg = true) +{ + NS_ABORT_IF_FALSE(gArgv, "gArgv must be initialized before CheckArg()"); + + char **curarg = gArgv + 1; // skip argv[0] + ArgResult ar = ARG_NONE; + + while (*curarg) { + char *arg = curarg[0]; + + if (arg[0] == '-' +#if defined(XP_WIN) + || *arg == '/' +#endif + ) { + ++arg; + if (*arg == '-') + ++arg; + + if (strimatch(aArg, arg)) { + if (aRemArg) + RemoveArg(curarg); + if (!aParam) { + ar = ARG_FOUND; + break; + } + + if (*curarg) { + if (**curarg == '-' +#if defined(XP_WIN) + || **curarg == '/' +#endif + ) + return ARG_BAD; + + *aParam = *curarg; + if (aRemArg) + RemoveArg(curarg); + ar = ARG_FOUND; + break; + } + return ARG_BAD; + } + } + + ++curarg; + } + + if (aCheckOSInt && ar == ARG_FOUND) { + ArgResult arOSInt = CheckArg("osint"); + if (arOSInt == ARG_FOUND) { + ar = ARG_BAD; + PR_fprintf(PR_STDERR, "Error: argument -osint is invalid\n"); + } + } + + return ar; +} + +#if defined(XP_WIN) +/** + * Check for a commandline flag from the windows shell and remove it from the + * argv used when restarting. Flags MUST be in the form -arg. + * + * @param aArg the parameter to check. Must be lowercase. + */ +static ArgResult +CheckArgShell(const char* aArg) +{ + char **curarg = gRestartArgv + 1; // skip argv[0] + + while (*curarg) { + char *arg = curarg[0]; + + if (arg[0] == '-') { + ++arg; + + if (strimatch(aArg, arg)) { + do { + *curarg = *(curarg + 1); + ++curarg; + } while (*curarg); + + --gRestartArgc; + + return ARG_FOUND; + } + } + + ++curarg; + } + + return ARG_NONE; +} + +/** + * Enabled Native App Support to process DDE messages when the app needs to + * restart and the app has been launched by the Windows shell to open an url. + * When aWait is false this will process the DDE events manually. This prevents + * Windows from displaying an error message due to the DDE message not being + * acknowledged. + */ +static void +ProcessDDE(nsINativeAppSupport* aNative, bool aWait) +{ + // When the app is launched by the windows shell the windows shell + // expects the app to be available for DDE messages and if it isn't + // windows displays an error dialog. To prevent the error the DDE server + // is enabled and pending events are processed when the app needs to + // restart after it was launched by the shell with the requestpending + // argument. The requestpending pending argument is removed to + // differentiate it from being launched when an app restart is not + // required. + ArgResult ar; + ar = CheckArgShell("requestpending"); + if (ar == ARG_FOUND) { + aNative->Enable(); // enable win32 DDE responses + if (aWait) { + nsIThread *thread = NS_GetCurrentThread(); + // This is just a guesstimate based on testing different values. + // If count is 8 or less windows will display an error dialog. + int32_t count = 20; + while(--count >= 0) { + NS_ProcessNextEvent(thread); + PR_Sleep(PR_MillisecondsToInterval(1)); + } + } + } +} +#endif + +/** + * Determines if there is support for showing the profile manager + * + * @return true in all environments except for Windows Metro +*/ +static bool +CanShowProfileManager() +{ +#if defined(XP_WIN) + return XRE_GetWindowsEnvironment() == WindowsEnvironmentType_Desktop; +#else + return true; +#endif +} + + +bool gSafeMode = false; + +/** + * The nsXULAppInfo object implements nsIFactory so that it can be its own + * singleton. + */ +class nsXULAppInfo : public nsIXULAppInfo, +#ifdef XP_WIN + public nsIWinAppHelper, +#endif +#ifdef MOZ_CRASHREPORTER + public nsICrashReporter, +#endif + public nsIXULRuntime + +{ +public: + MOZ_CONSTEXPR nsXULAppInfo() {} + NS_DECL_ISUPPORTS_INHERITED + NS_DECL_NSIXULAPPINFO + NS_DECL_NSIXULRUNTIME +#ifdef MOZ_CRASHREPORTER + NS_DECL_NSICRASHREPORTER +#endif +#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 +#ifdef MOZ_CRASHREPORTER + NS_INTERFACE_MAP_ENTRY(nsICrashReporter) +#endif + NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIXULAppInfo, gAppData || + XRE_GetProcessType() == GeckoProcessType_Content) +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_GetProcessType() == GeckoProcessType_Content) { + return NS_ERROR_NOT_AVAILABLE; + } + aResult.Assign(gAppData->vendor); + + return NS_OK; +} + +NS_IMETHODIMP +nsXULAppInfo::GetName(nsACString& aResult) +{ + if (XRE_GetProcessType() == GeckoProcessType_Content) { + ContentChild* cc = ContentChild::GetSingleton(); + aResult = cc->GetAppInfo().name; + return NS_OK; + } + aResult.Assign(gAppData->name); + + return NS_OK; +} + +NS_IMETHODIMP +nsXULAppInfo::GetID(nsACString& aResult) +{ + if (XRE_GetProcessType() == GeckoProcessType_Content) { + return NS_ERROR_NOT_AVAILABLE; + } + aResult.Assign(gAppData->ID); + + return NS_OK; +} + +NS_IMETHODIMP +nsXULAppInfo::GetVersion(nsACString& aResult) +{ + if (XRE_GetProcessType() == GeckoProcessType_Content) { + 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_GetProcessType() == GeckoProcessType_Content) { + 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_GetProcessType() == GeckoProcessType_Content) { + ContentChild* cc = ContentChild::GetSingleton(); + aResult = cc->GetAppInfo().UAName; + return NS_OK; + } + aResult.Assign(gAppData->UAName); + + 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(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) + +// .. and ensure that that is all of them: +static_assert(GeckoProcessType_IPDLUnitTest + 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; +} + +static bool gBrowserTabsRemote = false; +static bool gBrowserTabsRemoteInitialized = false; + +NS_IMETHODIMP +nsXULAppInfo::GetBrowserTabsRemote(bool* aResult) +{ + *aResult = BrowserTabsRemote(); + return NS_OK; +} + +NS_IMETHODIMP +nsXULAppInfo::EnsureContentProcess() +{ + if (XRE_GetProcessType() != GeckoProcessType_Default) + return NS_ERROR_NOT_AVAILABLE; + + nsRefPtr unused = ContentParent::GetNewOrUsed(); + return NS_OK; +} + +NS_IMETHODIMP +nsXULAppInfo::InvalidateCachesOnRestart() +{ + nsCOMPtr file; + nsresult rv = NS_GetSpecialDirectory(NS_APP_PROFILE_DIR_STARTUP, + getter_AddRefs(file)); + if (NS_FAILED(rv)) + return rv; + if (!file) + return NS_ERROR_NOT_AVAILABLE; + + file->AppendNative(FILE_COMPATIBILITY_INFO); + + nsINIParser parser; + rv = parser.Init(file); + if (NS_FAILED(rv)) { + // This fails if compatibility.ini is not there, so we'll + // flush the caches on the next restart anyways. + return NS_OK; + } + + nsAutoCString buf; + rv = parser.GetString("Compatibility", "InvalidateCaches", buf); + + if (NS_FAILED(rv)) { + PRFileDesc *fd = nullptr; + file->OpenNSPRFileDesc(PR_RDWR | PR_APPEND, 0600, &fd); + if (!fd) { + 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::GetLastRunCrashID(nsAString &aLastRunCrashID) +{ +#ifdef MOZ_CRASHREPORTER + CrashReporter::GetLastRunCrashID(aLastRunCrashID); + return NS_OK; +#else + return NS_ERROR_NOT_IMPLEMENTED; +#endif +} + +NS_IMETHODIMP +nsXULAppInfo::GetIsReleaseBuild(bool* aResult) +{ +#ifdef RELEASE_BUILD + *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(NS_STRINGIFY(MOZ_UPDATE_CHANNEL)); + return NS_OK; +} + +NS_IMETHODIMP +nsXULAppInfo::GetDistributionID(nsACString& aResult) +{ + aResult.AssignLiteral(MOZ_DISTRIBUTION_ID); + return NS_OK; +} + +#ifdef XP_WIN +// Matches the enum in WinNT.h for the Vista SDK but renamed so that we can +// safely build with the Vista SDK and without it. +typedef enum +{ + VistaTokenElevationTypeDefault = 1, + VistaTokenElevationTypeFull, + VistaTokenElevationTypeLimited +} VISTA_TOKEN_ELEVATION_TYPE; + +// avoid collision with TokeElevationType enum in WinNT.h +// of the Vista SDK +#define VistaTokenElevationType static_cast< TOKEN_INFORMATION_CLASS >( 18 ) + +NS_IMETHODIMP +nsXULAppInfo::GetUserCanElevate(bool *aUserCanElevate) +{ + HANDLE hToken; + + VISTA_TOKEN_ELEVATION_TYPE elevationType; + DWORD dwSize; + + if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hToken) || + !GetTokenInformation(hToken, VistaTokenElevationType, &elevationType, + sizeof(elevationType), &dwSize)) { + *aUserCanElevate = false; + } + else { + // 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 == VistaTokenElevationTypeLimited); + } + + if (hToken) + CloseHandle(hToken); + + return NS_OK; +} +#endif + +#ifdef MOZ_CRASHREPORTER +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 xreDirectory; + if (gAppData) { + xreDirectory = gAppData->xreDirectory; + } + else { + // We didn't get started through XRE_Main, probably + nsCOMPtr greDir; + NS_GetSpecialDirectory(NS_GRE_DIR, getter_AddRefs(greDir)); + if (!greDir) + return NS_ERROR_FAILURE; + + xreDirectory = do_QueryInterface(greDir); + if (!xreDirectory) + return NS_ERROR_FAILURE; + } + return CrashReporter::SetExceptionHandler(xreDirectory, true); + } + else { + if (!CrashReporter::GetEnabled()) + // no point in erroring for double-disabling + return NS_OK; + + return CrashReporter::UnsetExceptionHandler(); + } +} + +NS_IMETHODIMP +nsXULAppInfo::GetServerURL(nsIURL** aServerURL) +{ + if (!CrashReporter::GetEnabled()) + return NS_ERROR_NOT_INITIALIZED; + + nsAutoCString data; + if (!CrashReporter::GetServerURL(data)) { + return NS_ERROR_FAILURE; + } + nsCOMPtr uri; + NS_NewURI(getter_AddRefs(uri), data); + if (!uri) + return NS_ERROR_FAILURE; + + nsCOMPtr url; + url = do_QueryInterface(uri); + NS_ADDREF(*aServerURL = url); + + return NS_OK; +} + +NS_IMETHODIMP +nsXULAppInfo::SetServerURL(nsIURL* aServerURL) +{ + bool schemeOk; + // only allow https or http URLs + nsresult rv = aServerURL->SchemeIs("https", &schemeOk); + NS_ENSURE_SUCCESS(rv, rv); + if (!schemeOk) { + rv = aServerURL->SchemeIs("http", &schemeOk); + NS_ENSURE_SUCCESS(rv, rv); + + if (!schemeOk) + return NS_ERROR_INVALID_ARG; + } + nsAutoCString spec; + 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::AnnotateCrashReport(const nsACString& key, + const nsACString& data) +{ + return CrashReporter::AnnotateCrashReport(key, data); +} + +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_WIN32 + return CrashReporter::WriteMinidumpForException(static_cast(aExceptionInfo)); +#else + return NS_ERROR_NOT_IMPLEMENTED; +#endif +} + +NS_IMETHODIMP +nsXULAppInfo::AppendObjCExceptionInfoToAppNotes(void* aException) +{ +#ifdef XP_MACOSX + return CrashReporter::AppendObjCExceptionInfoToAppNotes(aException); +#else + return NS_ERROR_NOT_IMPLEMENTED; +#endif +} + +NS_IMETHODIMP +nsXULAppInfo::GetSubmitReports(bool* aEnabled) +{ + return CrashReporter::GetSubmitReports(aEnabled); +} + +NS_IMETHODIMP +nsXULAppInfo::SetSubmitReports(bool aEnabled) +{ + return CrashReporter::SetSubmitReports(aEnabled); +} + +NS_IMETHODIMP +nsXULAppInfo::UpdateCrashEventsDir() +{ + CrashReporter::UpdateCrashEventsDir(); + return NS_OK; +} + +#endif + +static const nsXULAppInfo kAppInfo; +static nsresult AppInfoConstructor(nsISupports* aOuter, + REFNSIID aIID, void **aResult) +{ + NS_ENSURE_NO_AGGREGATION(aOuter); + + return const_cast(&kAppInfo)-> + QueryInterface(aIID, aResult); +} + +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(); + nsresult SetWindowCreator(nsINativeAppSupport* native); + + static nsresult CreateAppSupport(nsISupports* aOuter, REFNSIID aIID, void** aResult); + +private: + nsIServiceManager* mServiceManager; + static nsINativeAppSupport* gNativeAppSupport; +}; + +ScopedXPCOMStartup::~ScopedXPCOMStartup() +{ + NS_IF_RELEASE(gNativeAppSupport); + + if (mServiceManager) { +#ifdef XP_MACOSX + // On OS X, we need a pool to catch cocoa objects that are autoreleased + // during teardown. + mozilla::MacAutoreleasePool pool; +#endif + + nsCOMPtr appStartup (do_GetService(NS_APPSTARTUP_CONTRACTID)); + if (appStartup) + appStartup->DestroyHiddenWindow(); + + gDirServiceProvider->DoShutdown(); + PROFILER_MARKER("Shutdown early"); + + WriteConsoleLog(); + + NS_ShutdownXPCOM(mServiceManager); + mServiceManager = nullptr; + } +} + +// {95d89e3e-a169-41a3-8e56-719978e15b12} +#define APPINFO_CID \ + { 0x95d89e3e, 0xa169, 0x41a3, { 0x8e, 0x56, 0x71, 0x99, 0x78, 0xe1, 0x5b, 0x12 } } + +// {0C4A446C-EE82-41f2-8D04-D366D2C7A7D4} +static const nsCID kNativeAppSupportCID = + { 0xc4a446c, 0xee82, 0x41f2, { 0x8d, 0x4, 0xd3, 0x66, 0xd2, 0xc7, 0xa7, 0xd4 } }; + +// {5F5E59CE-27BC-47eb-9D1F-B09CA9049836} +static const nsCID kProfileServiceCID = + { 0x5f5e59ce, 0x27bc, 0x47eb, { 0x9d, 0x1f, 0xb0, 0x9c, 0xa9, 0x4, 0x98, 0x36 } }; + +static already_AddRefed +ProfileServiceFactoryConstructor(const mozilla::Module& module, const mozilla::Module::CIDEntry& entry) +{ + nsCOMPtr factory; + NS_NewToolkitProfileFactory(getter_AddRefs(factory)); + return factory.forget(); +} + +NS_DEFINE_NAMED_CID(APPINFO_CID); + +static const mozilla::Module::CIDEntry kXRECIDs[] = { + { &kAPPINFO_CID, false, nullptr, AppInfoConstructor }, + { &kProfileServiceCID, false, ProfileServiceFactoryConstructor, nullptr }, + { &kNativeAppSupportCID, false, nullptr, ScopedXPCOMStartup::CreateAppSupport }, + { nullptr } +}; + +static const mozilla::Module::ContractIDEntry kXREContracts[] = { + { XULAPPINFO_SERVICE_CONTRACTID, &kAPPINFO_CID }, + { XULRUNTIME_SERVICE_CONTRACTID, &kAPPINFO_CID }, +#ifdef MOZ_CRASHREPORTER + { NS_CRASHREPORTER_CONTRACTID, &kAPPINFO_CID }, +#endif + { NS_PROFILESERVICE_CONTRACTID, &kProfileServiceCID }, + { NS_NATIVEAPPSUPPORT_CONTRACTID, &kNativeAppSupportCID }, + { nullptr } +}; + +static const mozilla::Module kXREModule = { + mozilla::Module::kVersion, + kXRECIDs, + kXREContracts +}; + +NSMODULE_DEFN(Apprunner) = &kXREModule; + +nsresult +ScopedXPCOMStartup::Initialize() +{ + NS_ASSERTION(gDirServiceProvider, "Should not get here!"); + + nsresult rv; + + rv = NS_InitXPCOM2(&mServiceManager, gDirServiceProvider->GetAppDir(), + gDirServiceProvider); + if (NS_FAILED(rv)) { + NS_ERROR("Couldn't start xpcom!"); + mServiceManager = nullptr; + } + else { + nsCOMPtr reg = + do_QueryInterface(mServiceManager); + NS_ASSERTION(reg, "Service Manager doesn't QI to Registrar."); + } + + return rv; +} + +/** + * This is a little factory class that serves as a singleton-service-factory + * for the nativeappsupport object. + */ +class nsSingletonFactory MOZ_FINAL : public nsIFactory +{ +public: + NS_DECL_ISUPPORTS + NS_DECL_NSIFACTORY + + nsSingletonFactory(nsISupports* aSingleton); + ~nsSingletonFactory() { } + +private: + nsCOMPtr 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); + + // Inform the chrome registry about OS accessibility + nsCOMPtr cr = + mozilla::services::GetToolkitChromeRegistryService(); + + if (cr) + cr->CheckForOSAccessibility(); + + nsCOMPtr creator (do_GetService(NS_APPSTARTUP_CONTRACTID)); + if (!creator) return NS_ERROR_UNEXPECTED; + + nsCOMPtr wwatch + (do_GetService(NS_WINDOWWATCHER_CONTRACTID, &rv)); + NS_ENSURE_SUCCESS(rv, rv); + + return wwatch->SetWindowCreator(creator); +} + +/* static */ nsresult +ScopedXPCOMStartup::CreateAppSupport(nsISupports* aOuter, REFNSIID aIID, void** aResult) +{ + if (aOuter) + return NS_ERROR_NO_AGGREGATION; + + if (!gNativeAppSupport) + return NS_ERROR_NOT_INITIALIZED; + + return gNativeAppSupport->QueryInterface(aIID, aResult); +} + +nsINativeAppSupport* ScopedXPCOMStartup::gNativeAppSupport; + +/** + * A helper class which calls NS_LogInit/NS_LogTerm in its scope. + */ +class ScopedLogging +{ +public: + ScopedLogging() { NS_LogInit(); } + ~ScopedLogging() { NS_LogTerm(); } +}; + +static void DumpArbitraryHelp() +{ + nsresult rv; + + ScopedLogging log; + + { + ScopedXPCOMStartup xpcom; + xpcom.Initialize(); + + nsCOMPtr cmdline + (do_CreateInstance("@mozilla.org/toolkit/command-line;1")); + if (!cmdline) + return; + + 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", gAppData->name); +#endif + + printf(" -h or -help Print this message.\n" + " -v or -version Print %s version.\n" + " -P Start with .\n" + " -migration Start with migration wizard.\n" + " -ProfileManager Start with ProfileManager.\n" + " -no-remote (default) Do not accept or send remote commands; implies -new-instance.\n" + " -allow-remote Accept and send remote commands.\n" + " -new-instance Open new instance, not a new window in running instance.\n" + " -UILocale Start with resources as UI Locale.\n" + " -safe-mode Disables extensions and themes for this session.\n", gAppData->name); + +#if defined(XP_WIN) + printf(" -console Start %s with a debugging console.\n", gAppData->name); +#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(); +} + +#if defined(DEBUG) && defined(XP_WIN) +#ifdef DEBUG_warren +#define _CRTDBG_MAP_ALLOC +#endif +// Set a CRT ReportHook function to capture and format MSCRT +// warnings, errors and assertions. +// See http://msdn.microsoft.com/en-US/library/74kabxyx(v=VS.80).aspx +#include +#include +#include "mozilla/mozalloc_abort.h" +static int MSCRTReportHook( int aReportType, char *aMessage, int *oReturnValue) +{ + *oReturnValue = 0; // continue execution + + // Do not use fprintf or other functions which may allocate + // memory from the heap which may be corrupted. Instead, + // use fputs to output the leading portion of the message + // and use mozalloc_abort to emit the remainder of the + // message. + + switch(aReportType) { + case 0: + fputs("\nWARNING: CRT WARNING", stderr); + fputs(aMessage, stderr); + fputs("\n", stderr); + break; + case 1: + fputs("\n###!!! ABORT: CRT ERROR ", stderr); + mozalloc_abort(aMessage); + break; + case 2: + fputs("\n###!!! ABORT: CRT ASSERT ", stderr); + mozalloc_abort(aMessage); + break; + } + + // do not invoke the debugger + return 1; +} + +#endif + +static inline void +DumpVersion() +{ + if (gAppData->vendor) + printf("%s ", gAppData->vendor); + printf("%s %s", gAppData->name, gAppData->version); + if (gAppData->copyright) + printf(", %s", gAppData->copyright); + printf("\n"); +} + +#ifdef MOZ_ENABLE_XREMOTE +// use int here instead of a PR type since it will be returned +// from main - just to keep types consistent +static int +HandleRemoteArgument(const char* remote, const char* aDesktopStartupID) +{ + nsresult rv; + ArgResult ar; + + const char *profile = 0; + nsAutoCString program(gAppData->name); + ToLowerCase(program); + const char *username = getenv("LOGNAME"); + + ar = CheckArg("p", false, &profile); + if (ar == ARG_BAD) { + PR_fprintf(PR_STDERR, "Error: argument -p requires a profile name\n"); + return 1; + } + + const char *temp = nullptr; + ar = CheckArg("a", false, &temp); + if (ar == ARG_BAD) { + PR_fprintf(PR_STDERR, "Error: argument -a requires an application name\n"); + return 1; + } else if (ar == ARG_FOUND) { + program.Assign(temp); + } + + ar = CheckArg("u", false, &username); + if (ar == ARG_BAD) { + PR_fprintf(PR_STDERR, "Error: argument -u requires a username\n"); + return 1; + } + + XRemoteClient client; + rv = client.Init(); + if (NS_FAILED(rv)) { + PR_fprintf(PR_STDERR, "Error: Failed to connect to X server.\n"); + return 1; + } + + nsXPIDLCString response; + bool success = false; + rv = client.SendCommand(program.get(), username, profile, remote, + aDesktopStartupID, getter_Copies(response), &success); + // did the command fail? + if (NS_FAILED(rv)) { + PR_fprintf(PR_STDERR, "Error: Failed to send command: %s\n", + response ? response.get() : "No response included"); + return 1; + } + + if (!success) { + PR_fprintf(PR_STDERR, "Error: No running window found\n"); + return 2; + } + + return 0; +} + +static RemoteResult +RemoteCommandLine(const char* aDesktopStartupID) +{ + nsresult rv; + ArgResult ar; + + nsAutoCString program(gAppData->name); + ToLowerCase(program); + const char *username = getenv("LOGNAME"); + + const char *temp = nullptr; + ar = CheckArg("a", true, &temp); + if (ar == ARG_BAD) { + PR_fprintf(PR_STDERR, "Error: argument -a requires an application name\n"); + return REMOTE_ARG_BAD; + } else if (ar == ARG_FOUND) { + program.Assign(temp); + } + + ar = CheckArg("u", true, &username); + if (ar == ARG_BAD) { + PR_fprintf(PR_STDERR, "Error: argument -u requires a username\n"); + return REMOTE_ARG_BAD; + } + + XRemoteClient client; + rv = client.Init(); + if (NS_FAILED(rv)) + return REMOTE_NOT_FOUND; + + nsXPIDLCString response; + bool success = false; + rv = client.SendCommandLine(program.get(), username, nullptr, + gArgc, gArgv, aDesktopStartupID, + getter_Copies(response), &success); + // did the command fail? + if (NS_FAILED(rv) || !success) + return REMOTE_NOT_FOUND; + + return REMOTE_FOUND; +} +#endif // MOZ_ENABLE_XREMOTE + +void +XRE_InitOmnijar(nsIFile* greOmni, nsIFile* appOmni) +{ + mozilla::Omnijar::Init(greOmni, appOmni); +} + +nsresult +XRE_GetBinaryPath(const char* argv0, nsIFile* *aResult) +{ + return mozilla::BinaryPath::GetFile(argv0, aResult); +} + +#ifdef XP_WIN +#include "nsWindowsRestart.cpp" +#include + +typedef BOOL (WINAPI* SetProcessDEPPolicyFunc)(DWORD dwFlags); +#endif + +// If aBlankCommandLine is true, then the application will be launched with a +// blank command line instead of being launched with the same command line that +// it was initially started with. +static nsresult LaunchChild(nsINativeAppSupport* aNative, + bool aBlankCommandLine = false) +{ + aNative->Quit(); // release DDE mutex, if we're holding it + + // 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_SLAVE"); +#endif + + if (aBlankCommandLine) { +#if defined(MOZ_WIDGET_QT) + // Remove only arguments not given to Qt + gRestartArgc = gQtOnlyArgc; + gRestartArgv = gQtOnlyArgv; +#else + gRestartArgc = 1; + gRestartArgv[gRestartArgc] = nullptr; +#endif + } + + SaveToEnv("MOZ_LAUNCHED_CHILD=1"); + +#if defined(MOZ_WIDGET_ANDROID) + mozilla::widget::android::GeckoAppShell::ScheduleRestart(); +#else +#if defined(XP_MACOSX) + CommandLineServiceMac::SetupMacCommandLine(gRestartArgc, gRestartArgv, true); + uint32_t restartMode = 0; + restartMode = gRestartMode; + LaunchChildMac(gRestartArgc, gRestartArgv, restartMode); +#else + nsCOMPtr lf; + nsresult rv = XRE_GetBinaryPath(gArgv[0], 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; + + if (!WinLaunchChild(exePath.get(), gRestartArgc, gRestartArgv)) + return NS_ERROR_FAILURE; + +#else + nsAutoCString exePath; + rv = lf->GetNativePath(exePath); + if (NS_FAILED(rv)) + return rv; + +#if defined(XP_UNIX) + if (execv(exePath.get(), gRestartArgv) == -1) + return NS_ERROR_FAILURE; +#else + PRProcess* process = PR_CreateProcess(exePath.get(), gRestartArgv, + nullptr, nullptr); + if (!process) return NS_ERROR_FAILURE; + + int32_t exitCode; + PRStatus failed = PR_WaitProcess(process, &exitCode); + if (failed || exitCode) + return NS_ERROR_FAILURE; +#endif // XP_UNIX +#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"; + +static nsresult +ProfileLockedDialog(nsIFile* aProfileDir, nsIFile* aProfileLocalDir, + nsIProfileUnlocker* aUnlocker, + nsINativeAppSupport* aNative, nsIProfileLock* *aResult) +{ + nsresult rv; + + 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 sbs = + mozilla::services::GetStringBundleService(); + NS_ENSURE_TRUE(sbs, NS_ERROR_FAILURE); + + nsCOMPtr sb; + sbs->CreateBundle(kProfileProperties, getter_AddRefs(sb)); + NS_ENSURE_TRUE_LOG(sbs, NS_ERROR_FAILURE); + + NS_ConvertUTF8toUTF16 appName(gAppData->name); + const char16_t* params[] = {appName.get(), appName.get()}; + + nsXPIDLString killMessage; +#ifndef XP_MACOSX + static const char16_t kRestartNoUnlocker[] = {'r','e','s','t','a','r','t','M','e','s','s','a','g','e','N','o','U','n','l','o','c','k','e','r','\0'}; // "restartMessageNoUnlocker" + static const char16_t kRestartUnlocker[] = {'r','e','s','t','a','r','t','M','e','s','s','a','g','e','U','n','l','o','c','k','e','r','\0'}; // "restartMessageUnlocker" +#else + static const char16_t kRestartNoUnlocker[] = {'r','e','s','t','a','r','t','M','e','s','s','a','g','e','N','o','U','n','l','o','c','k','e','r','M','a','c','\0'}; // "restartMessageNoUnlockerMac" + static const char16_t kRestartUnlocker[] = {'r','e','s','t','a','r','t','M','e','s','s','a','g','e','U','n','l','o','c','k','e','r','M','a','c','\0'}; // "restartMessageUnlockerMac" +#endif + + sb->FormatStringFromName(aUnlocker ? kRestartUnlocker : kRestartNoUnlocker, + params, 2, getter_Copies(killMessage)); + + nsXPIDLString killTitle; + sb->FormatStringFromName(MOZ_UTF16("restartTitle"), + params, 1, getter_Copies(killTitle)); + + if (!killMessage || !killTitle) + return NS_ERROR_FAILURE; + + nsCOMPtr ps + (do_GetService(NS_PROMPTSERVICE_CONTRACTID)); + NS_ENSURE_TRUE(ps, NS_ERROR_FAILURE); + + if (aUnlocker) { + int32_t button; +#ifdef MOZ_WIDGET_ANDROID + mozilla::widget::android::GeckoAppShell::KillAnyZombies(); + button = 1; +#else + const uint32_t flags = + (nsIPromptService::BUTTON_TITLE_CANCEL * + nsIPromptService::BUTTON_POS_0) + + (nsIPromptService::BUTTON_TITLE_IS_STRING * + nsIPromptService::BUTTON_POS_1) + + nsIPromptService::BUTTON_POS_1_DEFAULT; + + bool checkState = false; + rv = ps->ConfirmEx(nullptr, killTitle, killMessage, flags, + killTitle, nullptr, nullptr, nullptr, + &checkState, &button); + NS_ENSURE_SUCCESS_LOG(rv, rv); +#endif + + if (button == 1) { + rv = aUnlocker->Unlock(nsIProfileUnlocker::FORCE_QUIT); + if (NS_FAILED(rv)) + return rv; + + return NS_LockProfilePath(aProfileDir, aProfileLocalDir, + nullptr, aResult); + } + } else { +#ifdef MOZ_WIDGET_ANDROID + if (mozilla::widget::android::GeckoAppShell::UnlockProfile()) { + return NS_LockProfilePath(aProfileDir, aProfileLocalDir, + nullptr, aResult); + } +#else + rv = ps->Alert(nullptr, killTitle, killMessage); + NS_ENSURE_SUCCESS_LOG(rv, rv); +#endif + } + + return NS_ERROR_ABORT; + } +} + + +static nsresult +ProfileMissingDialog(nsINativeAppSupport* aNative) +{ + nsresult rv; + + ScopedXPCOMStartup xpcom; + rv = xpcom.Initialize(); + NS_ENSURE_SUCCESS(rv, rv); + + rv = xpcom.SetWindowCreator(aNative); + NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE); + + { //extra scoping is needed so we release these components before xpcom shutdown + nsCOMPtr sbs = + mozilla::services::GetStringBundleService(); + NS_ENSURE_TRUE(sbs, NS_ERROR_FAILURE); + + nsCOMPtr sb; + sbs->CreateBundle(kProfileProperties, getter_AddRefs(sb)); + NS_ENSURE_TRUE_LOG(sbs, NS_ERROR_FAILURE); + + NS_ConvertUTF8toUTF16 appName(gAppData->name); + const char16_t* params[] = {appName.get(), appName.get()}; + + nsXPIDLString missingMessage; + + // profileMissing + static const char16_t kMissing[] = {'p','r','o','f','i','l','e','M','i','s','s','i','n','g','\0'}; + sb->FormatStringFromName(kMissing, params, 2, getter_Copies(missingMessage)); + + nsXPIDLString missingTitle; + sb->FormatStringFromName(MOZ_UTF16("profileMissingTitle"), + params, 1, getter_Copies(missingTitle)); + + if (missingMessage && missingTitle) { + nsCOMPtr ps + (do_GetService(NS_PROMPTSERVICE_CONTRACTID)); + NS_ENSURE_TRUE(ps, NS_ERROR_FAILURE); + + ps->Alert(nullptr, missingTitle, missingMessage); + } + + return NS_ERROR_ABORT; + } +} + +static nsresult +ProfileLockedDialog(nsIToolkitProfile* aProfile, nsIProfileUnlocker* aUnlocker, + nsINativeAppSupport* aNative, nsIProfileLock* *aResult) +{ + nsCOMPtr profileDir; + nsresult rv = aProfile->GetRootDir(getter_AddRefs(profileDir)); + if (NS_FAILED(rv)) return rv; + + bool exists; + profileDir->Exists(&exists); + if (!exists) { + return ProfileMissingDialog(aNative); + } + + nsCOMPtr profileLocalDir; + rv = aProfile->GetLocalDir(getter_AddRefs(profileLocalDir)); + if (NS_FAILED(rv)) return rv; + + return ProfileLockedDialog(profileDir, profileLocalDir, aUnlocker, aNative, + aResult); +} + +static const char kProfileManagerURL[] = + "chrome://mozapps/content/profile/profileSelection.xul"; + +static nsresult +ShowProfileManager(nsIToolkitProfileService* aProfileSvc, + nsINativeAppSupport* aNative) +{ + if (!CanShowProfileManager()) { + return NS_ERROR_NOT_IMPLEMENTED; + } + + nsresult rv; + + nsCOMPtr profD, profLD; + char16_t* profileNamePtr; + nsAutoCString profileName; + + { + 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 + +#ifdef XP_WIN + // we don't have to wait here because profile manager window will pump + // and DDE message will be handled + ProcessDDE(aNative, false); +#endif + + { //extra scoping is needed so we release these components before xpcom shutdown + nsCOMPtr windowWatcher + (do_GetService(NS_WINDOWWATCHER_CONTRACTID)); + nsCOMPtr ioParamBlock + (do_CreateInstance(NS_DIALOGPARAMBLOCK_CONTRACTID)); + nsCOMPtr dlgArray (do_CreateInstance(NS_ARRAY_CONTRACTID)); + NS_ENSURE_TRUE(windowWatcher && ioParamBlock && dlgArray, NS_ERROR_FAILURE); + + ioParamBlock->SetObjects(dlgArray); + + nsCOMPtr appStartup + (do_GetService(NS_APPSTARTUP_CONTRACTID)); + NS_ENSURE_TRUE(appStartup, NS_ERROR_FAILURE); + + nsCOMPtr newWindow; + rv = windowWatcher->OpenWindow(nullptr, + kProfileManagerURL, + "_blank", + "centerscreen,chrome,modal,titlebar", + ioParamBlock, + getter_AddRefs(newWindow)); + + NS_ENSURE_SUCCESS_LOG(rv, rv); + + aProfileSvc->Flush(); + + int32_t dialogConfirmed; + rv = ioParamBlock->GetInt(0, &dialogConfirmed); + if (NS_FAILED(rv) || dialogConfirmed == 0) return NS_ERROR_ABORT; + + nsCOMPtr lock; + rv = dlgArray->QueryElementAt(0, NS_GET_IID(nsIProfileLock), + getter_AddRefs(lock)); + NS_ENSURE_SUCCESS_LOG(rv, rv); + + rv = lock->GetDirectory(getter_AddRefs(profD)); + NS_ENSURE_SUCCESS(rv, rv); + + rv = lock->GetLocalDirectory(getter_AddRefs(profLD)); + NS_ENSURE_SUCCESS(rv, rv); + + rv = ioParamBlock->GetString(0, &profileNamePtr); + NS_ENSURE_SUCCESS(rv, rv); + + CopyUTF16toUTF8(profileNamePtr, profileName); + NS_Free(profileNamePtr); + + lock->Unlock(); + } + } + + SaveFileToEnv("XRE_PROFILE_PATH", profD); + SaveFileToEnv("XRE_PROFILE_LOCAL_PATH", profLD); + SaveWordToEnv("XRE_PROFILE_NAME", profileName); + + bool offline = false; + aProfileSvc->GetStartOffline(&offline); + if (offline) { + SaveToEnv("XRE_START_OFFLINE=1"); + } + + return LaunchChild(aNative); +} + +static nsresult +GetCurrentProfileIsDefault(nsIToolkitProfileService* aProfileSvc, + nsIFile* aCurrentProfileRoot, bool *aResult) +{ + nsresult rv; + // Check that the profile to reset is the default since reset and the associated migration are + // only supported in that case. + nsCOMPtr selectedProfile; + nsCOMPtr selectedProfileRoot; + rv = aProfileSvc->GetSelectedProfile(getter_AddRefs(selectedProfile)); + NS_ENSURE_SUCCESS(rv, rv); + + rv = selectedProfile->GetRootDir(getter_AddRefs(selectedProfileRoot)); + NS_ENSURE_SUCCESS(rv, rv); + + bool currentIsSelected; + rv = aCurrentProfileRoot->Equals(selectedProfileRoot, ¤tIsSelected); + + *aResult = currentIsSelected; + return rv; +} + +/** + * Set the currently running profile as the default/selected one. + * + * @param aCurrentProfileRoot The root directory of the current profile. + * @return an error if aCurrentProfileRoot is not found or the profile could not + * be set as the default. + */ +static nsresult +SetCurrentProfileAsDefault(nsIToolkitProfileService* aProfileSvc, + nsIFile* aCurrentProfileRoot) +{ + NS_ENSURE_ARG_POINTER(aProfileSvc); + + nsCOMPtr profiles; + nsresult rv = aProfileSvc->GetProfiles(getter_AddRefs(profiles)); + if (NS_FAILED(rv)) + return rv; + + bool foundMatchingProfile = false; + nsCOMPtr supports; + rv = profiles->GetNext(getter_AddRefs(supports)); + while (NS_SUCCEEDED(rv)) { + nsCOMPtr profile = do_QueryInterface(supports); + nsCOMPtr profileRoot; + profile->GetRootDir(getter_AddRefs(profileRoot)); + profileRoot->Equals(aCurrentProfileRoot, &foundMatchingProfile); + if (foundMatchingProfile && profile) { + rv = aProfileSvc->SetSelectedProfile(profile); + if (NS_SUCCEEDED(rv)) + rv = aProfileSvc->Flush(); + return rv; + } + rv = profiles->GetNext(getter_AddRefs(supports)); + } + return rv; +} + +static bool gDoMigration = false; +static bool gDoProfileReset = false; + +// Pick a profile. We need to end up with a profile lock. +// +// 1) check for -profile +// 2) check for -P +// 3) check for -ProfileManager +// 4) use the default profile, if there is one +// 5) if there are *no* profiles, set up profile-migration +// 6) display the profile-manager UI +static nsresult +SelectProfile(nsIProfileLock* *aResult, nsIToolkitProfileService* aProfileSvc, nsINativeAppSupport* aNative, + bool* aStartOffline, nsACString* aProfileName) +{ + StartupTimeline::Record(StartupTimeline::SELECT_PROFILE); + + nsresult rv; + ArgResult ar; + const char* arg; + *aResult = nullptr; + *aStartOffline = false; + + ar = CheckArg("offline", true); + if (ar == ARG_BAD) { + PR_fprintf(PR_STDERR, "Error: argument -offline is invalid when argument -osint is specified\n"); + return NS_ERROR_FAILURE; + } + + if (ar || EnvHasValue("XRE_START_OFFLINE")) + *aStartOffline = true; + + if (EnvHasValue("MOZ_RESET_PROFILE_RESTART")) { + gDoProfileReset = true; + gDoMigration = true; + SaveToEnv("MOZ_RESET_PROFILE_RESTART="); + } + + // reset-profile and migration args need to be checked before any profiles are chosen below. + ar = CheckArg("reset-profile", true); + if (ar == ARG_BAD) { + PR_fprintf(PR_STDERR, "Error: argument -reset-profile is invalid when argument -osint is specified\n"); + return NS_ERROR_FAILURE; + } else if (ar == ARG_FOUND) { + gDoProfileReset = true; + } + + ar = CheckArg("migration", true); + if (ar == ARG_BAD) { + PR_fprintf(PR_STDERR, "Error: argument -migration is invalid when argument -osint is specified\n"); + return NS_ERROR_FAILURE; + } else if (ar == ARG_FOUND) { + gDoMigration = true; + } + + nsCOMPtr lf = GetFileFromEnv("XRE_PROFILE_PATH"); + if (lf) { + nsCOMPtr localDir = + GetFileFromEnv("XRE_PROFILE_LOCAL_PATH"); + if (!localDir) { + localDir = lf; + } + + arg = PR_GetEnv("XRE_PROFILE_NAME"); + if (arg && *arg && aProfileName) + aProfileName->Assign(nsDependentCString(arg)); + + // Clear out flags that we handled (or should have handled!) last startup. + const char *dummy; + CheckArg("p", false, &dummy); + CheckArg("profile", false, &dummy); + CheckArg("profilemanager"); + + if (gDoProfileReset) { + // Check that the profile to reset is the default since reset and migration are only + // supported in that case. + bool currentIsSelected; + GetCurrentProfileIsDefault(aProfileSvc, lf, ¤tIsSelected); + if (!currentIsSelected) { + NS_WARNING("Profile reset is only supported for the default profile."); + gDoProfileReset = gDoMigration = false; + } + + // If we're resetting a profile, create a new one and use it to startup. + nsCOMPtr newProfile; + rv = CreateResetProfile(aProfileSvc, getter_AddRefs(newProfile)); + if (NS_SUCCEEDED(rv)) { + rv = newProfile->GetRootDir(getter_AddRefs(lf)); + NS_ENSURE_SUCCESS(rv, rv); + SaveFileToEnv("XRE_PROFILE_PATH", lf); + + rv = newProfile->GetLocalDir(getter_AddRefs(localDir)); + NS_ENSURE_SUCCESS(rv, rv); + SaveFileToEnv("XRE_PROFILE_LOCAL_PATH", localDir); + + rv = newProfile->GetName(*aProfileName); + if (NS_FAILED(rv)) + aProfileName->Truncate(0); + SaveWordToEnv("XRE_PROFILE_NAME", *aProfileName); + } else { + NS_WARNING("Profile reset failed."); + gDoProfileReset = false; + } + } + + return NS_LockProfilePath(lf, localDir, nullptr, aResult); + } + + ar = CheckArg("profile", true, &arg); + if (ar == ARG_BAD) { + PR_fprintf(PR_STDERR, "Error: argument -profile requires a path\n"); + return NS_ERROR_FAILURE; + } + if (ar) { + if (gDoProfileReset) { + NS_WARNING("Profile reset is only supported for the default profile."); + gDoProfileReset = false; + } + + nsCOMPtr lf; + rv = XRE_GetFileFromPath(arg, getter_AddRefs(lf)); + NS_ENSURE_SUCCESS(rv, rv); + + nsCOMPtr unlocker; + + // Check if the profile path exists and it's a directory. + bool exists; + lf->Exists(&exists); + if (!exists) { + rv = lf->Create(nsIFile::DIRECTORY_TYPE, 0700); + NS_ENSURE_SUCCESS(rv, rv); + } + + // If a profile path is specified directory on the command line, then + // assume that the temp directory is the same as the given directory. + rv = NS_LockProfilePath(lf, lf, getter_AddRefs(unlocker), aResult); + if (NS_SUCCEEDED(rv)) + return rv; + + return ProfileLockedDialog(lf, lf, unlocker, aNative, aResult); + } + + ar = CheckArg("createprofile", true, &arg); + if (ar == ARG_BAD) { + PR_fprintf(PR_STDERR, "Error: argument -createprofile requires a profile name\n"); + return NS_ERROR_FAILURE; + } + if (ar) { + nsCOMPtr profile; + + const char* delim = strchr(arg, ' '); + if (delim) { + nsCOMPtr lf; + rv = NS_NewNativeLocalFile(nsDependentCString(delim + 1), + true, getter_AddRefs(lf)); + if (NS_FAILED(rv)) { + PR_fprintf(PR_STDERR, "Error: profile path not valid.\n"); + return rv; + } + + // As with -profile, assume that the given path will be used for the + // main profile directory. + rv = aProfileSvc->CreateProfile(lf, nsDependentCSubstring(arg, delim), + getter_AddRefs(profile)); + } else { + rv = aProfileSvc->CreateProfile(nullptr, nsDependentCString(arg), + getter_AddRefs(profile)); + } + // Some pathological arguments can make it this far + if (NS_FAILED(rv)) { + PR_fprintf(PR_STDERR, "Error creating profile.\n"); + return rv; + } + rv = NS_ERROR_ABORT; + aProfileSvc->Flush(); + + // XXXben need to ensure prefs.js exists here so the tinderboxes will + // not go orange. + nsCOMPtr prefsJSFile; + profile->GetRootDir(getter_AddRefs(prefsJSFile)); + prefsJSFile->AppendNative(NS_LITERAL_CSTRING("prefs.js")); + nsAutoCString pathStr; + prefsJSFile->GetNativePath(pathStr); + PR_fprintf(PR_STDERR, "Success: created profile '%s' at '%s'\n", arg, pathStr.get()); + bool exists; + prefsJSFile->Exists(&exists); + if (!exists) + prefsJSFile->Create(nsIFile::NORMAL_FILE_TYPE, 0644); + // XXXdarin perhaps 0600 would be better? + + return rv; + } + + uint32_t count; + rv = aProfileSvc->GetProfileCount(&count); + NS_ENSURE_SUCCESS(rv, rv); + + ar = CheckArg("p", false, &arg); + if (ar == ARG_BAD) { + ar = CheckArg("osint"); + if (ar == ARG_FOUND) { + PR_fprintf(PR_STDERR, "Error: argument -p is invalid when argument -osint is specified\n"); + return NS_ERROR_FAILURE; + } + + if (CanShowProfileManager()) { + return ShowProfileManager(aProfileSvc, aNative); + } + } + if (ar) { + ar = CheckArg("osint"); + if (ar == ARG_FOUND) { + PR_fprintf(PR_STDERR, "Error: argument -p is invalid when argument -osint is specified\n"); + return NS_ERROR_FAILURE; + } + nsCOMPtr profile; + rv = aProfileSvc->GetProfileByName(nsDependentCString(arg), + getter_AddRefs(profile)); + if (NS_SUCCEEDED(rv)) { + if (gDoProfileReset) { + NS_WARNING("Profile reset is only supported for the default profile."); + gDoProfileReset = false; + } + + nsCOMPtr unlocker; + rv = profile->Lock(getter_AddRefs(unlocker), aResult); + if (NS_SUCCEEDED(rv)) { + if (aProfileName) + aProfileName->Assign(nsDependentCString(arg)); + return NS_OK; + } + + return ProfileLockedDialog(profile, unlocker, aNative, aResult); + } + + if (CanShowProfileManager()) { + return ShowProfileManager(aProfileSvc, aNative); + } + } + + ar = CheckArg("profilemanager", true); + if (ar == ARG_BAD) { + PR_fprintf(PR_STDERR, "Error: argument -profilemanager is invalid when argument -osint is specified\n"); + return NS_ERROR_FAILURE; + } else if (ar == ARG_FOUND && CanShowProfileManager()) { + return ShowProfileManager(aProfileSvc, aNative); + } + + if (!count) { + gDoMigration = true; + gDoProfileReset = false; + + // create a default profile + nsCOMPtr profile; + nsresult rv = aProfileSvc->CreateProfile(nullptr, // choose a default dir for us + NS_LITERAL_CSTRING("default"), + getter_AddRefs(profile)); + if (NS_SUCCEEDED(rv)) { + aProfileSvc->Flush(); + rv = profile->Lock(nullptr, aResult); + if (NS_SUCCEEDED(rv)) { + if (aProfileName) + aProfileName->Assign(NS_LITERAL_CSTRING("default")); + return NS_OK; + } + } + } + + bool useDefault = true; + if (count > 1 && CanShowProfileManager()) { + aProfileSvc->GetStartWithLastProfile(&useDefault); + } + + if (useDefault) { + nsCOMPtr profile; + // GetSelectedProfile will auto-select the only profile if there's just one + aProfileSvc->GetSelectedProfile(getter_AddRefs(profile)); + if (profile) { + // If we're resetting a profile, create a new one and use it to startup. + if (gDoProfileReset) { + { + // Check that the source profile is not in use by temporarily acquiring its lock. + nsIProfileLock* tempProfileLock; + nsCOMPtr unlocker; + rv = profile->Lock(getter_AddRefs(unlocker), &tempProfileLock); + if (NS_FAILED(rv)) + return ProfileLockedDialog(profile, unlocker, aNative, &tempProfileLock); + } + + nsCOMPtr newProfile; + rv = CreateResetProfile(aProfileSvc, getter_AddRefs(newProfile)); + if (NS_SUCCEEDED(rv)) + profile = newProfile; + else + gDoProfileReset = false; + } + + // 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; + + nsCOMPtr unlocker; + const TimeStamp start = TimeStamp::Now(); + do { + rv = profile->Lock(getter_AddRefs(unlocker), aResult); + if (NS_SUCCEEDED(rv)) { + StartupTimeline::Record(StartupTimeline::AFTER_PROFILE_LOCKED); + // Try to grab the profile name. + if (aProfileName) { + rv = profile->GetName(*aProfileName); + if (NS_FAILED(rv)) + aProfileName->Truncate(0); + } + return NS_OK; + } + PR_Sleep(kLockRetrySleepMS); + } while (TimeStamp::Now() - start < TimeDuration::FromSeconds(kLockRetrySeconds)); + + return ProfileLockedDialog(profile, unlocker, aNative, aResult); + } + } + + if (!CanShowProfileManager()) { + return NS_ERROR_FAILURE; + } + + return ShowProfileManager(aProfileSvc, aNative); +} + +/** + * 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. + */ +static bool +CheckCompatibility(nsIFile* aProfileDir, const nsCString& aVersion, + const nsCString& aOSABI, nsIFile* aXULRunnerDir, + nsIFile* aAppDir, nsIFile* aFlagFile, + bool* aCachesOK) +{ + *aCachesOK = false; + nsCOMPtr file; + aProfileDir->Clone(getter_AddRefs(file)); + if (!file) + return false; + file->AppendNative(FILE_COMPATIBILITY_INFO); + + nsINIParser parser; + nsresult rv = parser.Init(file); + if (NS_FAILED(rv)) + return false; + + nsAutoCString buf; + rv = parser.GetString("Compatibility", "LastVersion", buf); + if (NS_FAILED(rv) || !aVersion.Equals(buf)) + return false; + + rv = parser.GetString("Compatibility", "LastOSABI", buf); + if (NS_FAILED(rv) || !aOSABI.Equals(buf)) + return false; + + rv = parser.GetString("Compatibility", "LastPlatformDir", buf); + if (NS_FAILED(rv)) + return false; + + nsCOMPtr lf; + rv = NS_NewNativeLocalFile(buf, false, + getter_AddRefs(lf)); + 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(buf, false, + getter_AddRefs(lf)); + 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) { + aFlagFile->Exists(&purgeCaches); + } + + *aCachesOK = !purgeCaches && *aCachesOK; + return true; +} + +static void BuildVersion(nsCString &aBuf) +{ + aBuf.Assign(gAppData->version); + aBuf.Append('_'); + aBuf.Append(gAppData->buildID); + aBuf.Append('/'); + aBuf.Append(gToolkitBuildID); +} + +static void +WriteVersion(nsIFile* aProfileDir, const nsCString& aVersion, + const nsCString& aOSABI, nsIFile* aXULRunnerDir, + nsIFile* aAppDir, bool invalidateCache) +{ + nsCOMPtr file; + aProfileDir->Clone(getter_AddRefs(file)); + if (!file) + return; + file->AppendNative(FILE_COMPATIBILITY_INFO); + + nsAutoCString platformDir; + aXULRunnerDir->GetNativePath(platformDir); + + nsAutoCString appDir; + if (aAppDir) + aAppDir->GetNativePath(appDir); + + PRFileDesc *fd = nullptr; + file->OpenNSPRFileDesc(PR_WRONLY | PR_CREATE_FILE | PR_TRUNCATE, 0600, &fd); + if (!fd) { + NS_ERROR("could not create output stream"); + return; + } + + static const char kHeader[] = "[Compatibility]" NS_LINEBREAK + "LastVersion="; + + PR_Write(fd, kHeader, sizeof(kHeader) - 1); + PR_Write(fd, aVersion.get(), aVersion.Length()); + + static const char kOSABIHeader[] = NS_LINEBREAK "LastOSABI="; + PR_Write(fd, kOSABIHeader, sizeof(kOSABIHeader) - 1); + PR_Write(fd, aOSABI.get(), aOSABI.Length()); + + static const char kPlatformDirHeader[] = NS_LINEBREAK "LastPlatformDir="; + + PR_Write(fd, kPlatformDirHeader, sizeof(kPlatformDirHeader) - 1); + PR_Write(fd, platformDir.get(), platformDir.Length()); + + static const char kAppDirHeader[] = NS_LINEBREAK "LastAppDir="; + if (aAppDir) { + PR_Write(fd, kAppDirHeader, sizeof(kAppDirHeader) - 1); + PR_Write(fd, appDir.get(), appDir.Length()); + } + + static const char kInvalidationHeader[] = NS_LINEBREAK "InvalidateCaches=1"; + if (invalidateCache) + PR_Write(fd, kInvalidationHeader, sizeof(kInvalidationHeader) - 1); + + static const char kNL[] = NS_LINEBREAK; + PR_Write(fd, kNL, sizeof(kNL) - 1); + + PR_Close(fd); +} + +/** + * Returns true if the startup cache file was successfully removed. + * Returns false if file->Clone fails at any point (OOM) or if unable + * to remove the startup cache file. Note in particular the return value + * is unaffected by a failure to remove extensions.ini + */ +static bool +RemoveComponentRegistries(nsIFile* aProfileDir, nsIFile* aLocalProfileDir, + bool aRemoveEMFiles) +{ + nsCOMPtr file; + aProfileDir->Clone(getter_AddRefs(file)); + if (!file) + return false; + + if (aRemoveEMFiles) { + file->SetNativeLeafName(NS_LITERAL_CSTRING("extensions.ini")); + 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(NS_LITERAL_CSTRING("XUL" PLATFORM_FASL_SUFFIX)); + file->Remove(false); + + file->SetNativeLeafName(NS_LITERAL_CSTRING("XPC" PLATFORM_FASL_SUFFIX)); + file->Remove(false); + + file->SetNativeLeafName(NS_LITERAL_CSTRING("startupCache")); + nsresult rv = file->Remove(true); + return NS_SUCCEEDED(rv) || rv == NS_ERROR_FILE_TARGET_DOES_NOT_EXIST; +} + +// To support application initiated restart via nsIAppStartup.quit, we +// need to save various environment variables, and then restore them +// before re-launching the application. + +static struct SavedVar { + const char *name; + char *value; +} gSavedVars[] = { + {"XUL_APP_FILE", nullptr} +}; + +static void SaveStateForAppInitiatedRestart() +{ + for (size_t i = 0; i < ArrayLength(gSavedVars); ++i) { + const char *s = PR_GetEnv(gSavedVars[i].name); + if (s) + gSavedVars[i].value = PR_smprintf("%s=%s", gSavedVars[i].name, s); + } +} + +static void RestoreStateForAppInitiatedRestart() +{ + for (size_t i = 0; i < ArrayLength(gSavedVars); ++i) { + if (gSavedVars[i].value) + PR_SetEnv(gSavedVars[i].value); + } +} + +#ifdef MOZ_CRASHREPORTER +// When we first initialize the crash reporter we don't have a profile, +// so we set the minidump path to $TEMP. Once we have a profile, +// we set it to $PROFILE/minidumps, creating the directory +// if needed. +static void MakeOrSetMinidumpPath(nsIFile* profD) +{ + nsCOMPtr dumpD; + profD->Clone(getter_AddRefs(dumpD)); + + if(dumpD) { + bool fileExists; + //XXX: do some more error checking here + dumpD->Append(NS_LITERAL_STRING("minidumps")); + dumpD->Exists(&fileExists); + if(!fileExists) { + dumpD->Create(nsIFile::DIRECTORY_TYPE, 0700); + } + + nsAutoString pathStr; + if(NS_SUCCEEDED(dumpD->GetPath(pathStr))) + CrashReporter::SetMinidumpPath(pathStr); + } +} +#endif + +const nsXREAppData* gAppData = nullptr; + +#ifdef MOZ_WIDGET_GTK +#include "prlink.h" +typedef void (*_g_set_application_name_fn)(const gchar *application_name); +typedef void (*_gtk_window_set_auto_startup_notification_fn)(gboolean setting); + +static PRFuncPtr FindFunction(const char* aName) +{ + PRLibrary *lib = nullptr; + PRFuncPtr result = PR_FindFunctionSymbolAndLibrary(aName, &lib); + // Since the library was already loaded, we can safely unload it here. + if (lib) { + PR_UnloadLibrary(lib); + } + return result; +} + +static void MOZ_gdk_display_close(GdkDisplay *display) +{ +#if CLEANUP_MEMORY + // XXX wallpaper for bug 417163: don't close the Display if we're using the + // Qt theme because we crash (in Qt code) when using jemalloc. + bool theme_is_qt = false; + GtkSettings* settings = + gtk_settings_get_for_screen(gdk_display_get_default_screen(display)); + gchar *theme_name; + g_object_get(settings, "gtk-theme-name", &theme_name, nullptr); + if (theme_name) { + theme_is_qt = strcmp(theme_name, "Qt") == 0; + if (theme_is_qt) + NS_WARNING("wallpaper bug 417163 for Qt theme"); + g_free(theme_name); + } + + // Get a (new) Pango context that holds a reference to the fontmap that + // GTK has been using. gdk_pango_context_get() must be called while GTK + // has a default display. + PangoContext *pangoContext = gdk_pango_context_get(); + + bool buggyCairoShutdown = cairo_version() < CAIRO_VERSION_ENCODE(1, 4, 0); + + if (!buggyCairoShutdown) { + // We should shut down GDK before we shut down libraries it depends on + // like Pango and cairo. But if cairo shutdown is buggy, we should + // shut down cairo first otherwise it may crash because of dangling + // references to Display objects (see bug 469831). + if (!theme_is_qt) + gdk_display_close(display); + } + + // Clean up PangoCairo's default fontmap. + // This pango_fc_font_map_shutdown call (and the associated code to + // get the font map) really shouldn't be needed anymore, except that + // it's needed to avoid having cairo_debug_reset_static_data fatally + // assert if we've leaked other things that hold on to the fontmap, + // which is something that currently happens in mochitest-plugins. + // Even if it didn't happen in mochitest-plugins, we probably want to + // avoid the crash-on-leak problem since it makes it harder to use + // many of our leak tools to debug leaks. + + // This doesn't take a reference. + PangoFontMap *fontmap = pango_context_get_font_map(pangoContext); + // Do some shutdown of the fontmap, which releases the fonts, clearing a + // bunch of circular references from the fontmap through the fonts back to + // itself. The shutdown that this does is much less than what's done by + // the fontmap's finalize, though. + if (PANGO_IS_FC_FONT_MAP(fontmap)) + pango_fc_font_map_shutdown(PANGO_FC_FONT_MAP(fontmap)); + g_object_unref(pangoContext); + + // Tell PangoCairo to release its default fontmap. + pango_cairo_font_map_set_default(nullptr); + + // cairo_debug_reset_static_data() is prototyped through cairo.h included + // by gtk.h. +#ifdef cairo_debug_reset_static_data +#error "Looks like we're including Mozilla's cairo instead of system cairo" +#endif + cairo_debug_reset_static_data(); + // FIXME: Do we need to call this in non-GTK2 cases as well? + FcFini(); + + if (buggyCairoShutdown) { + if (!theme_is_qt) + gdk_display_close(display); + } +#else // not CLEANUP_MEMORY + // Don't do anything to avoid running into driver bugs under XCloseDisplay(). + // See bug 973192. + (void) display; +#endif +} +#endif // MOZ_WIDGET_GTK2 + +/** + * 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 + +#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 InitDwriteBG(LPVOID lpdwThreadParam) +{ + SetThreadPriority(GetCurrentThread(), THREAD_MODE_BACKGROUND_BEGIN); + LOGREGISTRY(L"loading dwrite.dll"); + HMODULE dwdll = LoadLibraryW(L"dwrite.dll"); + if (dwdll) { + decltype(DWriteCreateFactory)* createDWriteFactory = (decltype(DWriteCreateFactory)*) + GetProcAddress(dwdll, "DWriteCreateFactory"); + if (createDWriteFactory) { + LOGREGISTRY(L"creating dwrite factory"); + IDWriteFactory *factory; + HRESULT hr = createDWriteFactory( + DWRITE_FACTORY_TYPE_SHARED, + __uuidof(IDWriteFactory), + reinterpret_cast(&factory)); + if (SUCCEEDED(hr)) { + LOGREGISTRY(L"dwrite factory done"); + factory->Release(); + LOGREGISTRY(L"freed factory"); + } else { + LOGREGISTRY(L"failed to create factory"); + } + } + } + SetThreadPriority(GetCurrentThread(), THREAD_MODE_BACKGROUND_END); + return 0; +} +#endif + +#ifdef USE_GLX_TEST +bool fire_glxtest_process(); +#endif + +#if defined(XP_WIN) && defined(MOZ_METRO) +#ifndef AHE_TYPE +enum AHE_TYPE { + AHE_DESKTOP = 0, + AHE_IMMERSIVE = 1 +}; +#endif + +/* + * The Windows launcher uses this value to decide what front end ui + * to launch. We always launch the same ui unless the user + * specifically asks to switch. Update the value on every startup + * based on the environment requested. + */ +void +SetLastWinRunType(AHE_TYPE aType) +{ + HKEY key; + LONG result = RegOpenKeyExW(HKEY_CURRENT_USER, + L"SOFTWARE\\Mozilla\\Firefox", + 0, KEY_WRITE, &key); + if (result != ERROR_SUCCESS) { + return; + } + DWORD value = (DWORD)aType; + result = RegSetValueEx(key, L"MetroLastAHE", 0, REG_DWORD, + reinterpret_cast(&value), + sizeof(DWORD)); + RegCloseKey(key); +} +#endif // defined(XP_WIN) && defined(MOZ_METRO) + +#include "GeckoProfiler.h" + +// Encapsulates startup and shutdown state for XRE_main +class XREMain +{ +public: + XREMain() : + mScopedXPCom(nullptr) + , mAppData(nullptr) + , mStartOffline(false) + , mShuttingDown(false) +#ifdef MOZ_ENABLE_XREMOTE + , mDisableRemote(false) +#endif +#if defined(MOZ_WIDGET_GTK) + , mGdkDisplay(nullptr) +#endif + {}; + + ~XREMain() { + if (mAppData) { + delete mAppData; + } + if (mScopedXPCom) { + NS_WARNING("Scoped xpcom should have been deleted!"); + delete mScopedXPCom; + } + } + + int XRE_main(int argc, char* argv[], const nsXREAppData* aAppData); + int XRE_mainInit(bool* aExitFlag); + int XRE_mainStartup(bool* aExitFlag); + nsresult XRE_mainRun(); + + nsCOMPtr mNativeApp; + nsCOMPtr mProfileSvc; + nsCOMPtr mProfD; + nsCOMPtr mProfLD; + nsCOMPtr mProfileLock; +#ifdef MOZ_ENABLE_XREMOTE + nsCOMPtr mRemoteService; +#endif + + ScopedXPCOMStartup* mScopedXPCom; + ScopedAppData* mAppData; + nsXREDirProvider mDirProvider; + nsAutoCString mProfileName; + nsAutoCString mDesktopStartupID; + + bool mStartOffline; + bool mShuttingDown; +#ifdef MOZ_ENABLE_XREMOTE + bool mDisableRemote; +#endif + +#if defined(MOZ_WIDGET_GTK) + GdkDisplay* mGdkDisplay; +#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; + + StartupTimeline::Record(StartupTimeline::MAIN); + + nsresult rv; + ArgResult ar; + +#ifdef DEBUG + if (PR_GetEnv("XRE_MAIN_BREAK")) + NS_BREAK(); +#endif + +#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 + +#if defined(XP_WIN) && defined(MOZ_METRO) + // Don't remove this arg, we want to pass it on to nsUpdateDriver + if (CheckArg("metro-update", false, nullptr, false) == ARG_FOUND || + XRE_GetWindowsEnvironment() == WindowsEnvironmentType_Metro) { + // If we're doing a restart update that was initiated from metro land, + // we'll be running desktop to handle the actual update. Request that + // after the restart we launch into metro. + SetLastWinRunType(AHE_IMMERSIVE); + } else { + SetLastWinRunType(AHE_DESKTOP); + } +#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. + + if (IsVistaOrLater()) { + CreateThread(nullptr, 0, (LPTHREAD_START_ROUTINE)&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", true, &override); + if (ar == ARG_BAD) { + Output(true, "Incorrect number of arguments passed to -override"); + return 1; + } + else if (ar == ARG_FOUND) { + nsCOMPtr overrideLF; + rv = XRE_GetFileFromPath(override, getter_AddRefs(overrideLF)); + if (NS_FAILED(rv)) { + Output(true, "Error: unrecognized override.ini path.\n"); + return 1; + } + + rv = XRE_ParseAppData(overrideLF, mAppData); + if (NS_FAILED(rv)) { + Output(true, "Couldn't read override.ini"); + return 1; + } + } + + // Check sanity and correctness of app data. + + if (!mAppData->name) { + Output(true, "Error: App:Name not specified in application.ini\n"); + return 1; + } + if (!mAppData->buildID) { + Output(true, "Error: App:BuildID not specified in application.ini\n"); + return 1; + } + + // XXX Originally ScopedLogging was here? Now it's in XRE_main above + // XRE_mainInit. + + if (!mAppData->xreDirectory) { + nsCOMPtr lf; + rv = XRE_GetBinaryPath(gArgv[0], getter_AddRefs(lf)); + if (NS_FAILED(rv)) + return 2; + + nsCOMPtr greDir; + rv = lf->GetParent(getter_AddRefs(greDir)); + if (NS_FAILED(rv)) + return 2; + + greDir.forget(&mAppData->xreDirectory); + } + + if (!mAppData->directory) { + NS_IF_ADDREF(mAppData->directory = mAppData->xreDirectory); + } + + if (mAppData->size > offsetof(nsXREAppData, minVersion)) { + 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! + SetAllocatedString(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", + gToolkitVersion, + mAppData->minVersion, mAppData->maxVersion); + return 1; + } + } + + rv = mDirProvider.Initialize(mAppData->directory, mAppData->xreDirectory); + if (NS_FAILED(rv)) + return 1; + +#ifdef MOZ_CRASHREPORTER + if (EnvHasValue("MOZ_CRASHREPORTER")) { + mAppData->flags |= NS_XRE_ENABLE_CRASH_REPORTER; + } + + if ((mAppData->flags & NS_XRE_ENABLE_CRASH_REPORTER) && + NS_SUCCEEDED( + CrashReporter::SetExceptionHandler(mAppData->xreDirectory))) { + CrashReporter::UpdateCrashEventsDir(); + if (mAppData->crashReporterURL) + CrashReporter::SetServerURL(nsDependentCString(mAppData->crashReporterURL)); + + // pass some basic info from the app data + if (mAppData->vendor) + CrashReporter::AnnotateCrashReport(NS_LITERAL_CSTRING("Vendor"), + nsDependentCString(mAppData->vendor)); + if (mAppData->name) + CrashReporter::AnnotateCrashReport(NS_LITERAL_CSTRING("ProductName"), + nsDependentCString(mAppData->name)); + if (mAppData->ID) + CrashReporter::AnnotateCrashReport(NS_LITERAL_CSTRING("ProductID"), + nsDependentCString(mAppData->ID)); + if (mAppData->version) + CrashReporter::AnnotateCrashReport(NS_LITERAL_CSTRING("Version"), + nsDependentCString(mAppData->version)); + if (mAppData->buildID) + CrashReporter::AnnotateCrashReport(NS_LITERAL_CSTRING("BuildID"), + nsDependentCString(mAppData->buildID)); + + nsDependentCString releaseChannel(NS_STRINGIFY(MOZ_UPDATE_CHANNEL)); + CrashReporter::AnnotateCrashReport(NS_LITERAL_CSTRING("ReleaseChannel"), + releaseChannel); +#ifdef MOZ_LINKER + CrashReporter::AnnotateCrashReport(NS_LITERAL_CSTRING("CrashAddressLikelyWrong"), + IsSignalHandlingBroken() ? NS_LITERAL_CSTRING("1") + : NS_LITERAL_CSTRING("0")); +#endif + CrashReporter::SetRestartArgs(gArgc, gArgv); + + // annotate other data (user id etc) + nsCOMPtr userAppDataDir; + if (NS_SUCCEEDED(mDirProvider.GetUserAppDataDirectory( + getter_AddRefs(userAppDataDir)))) { + CrashReporter::SetupExtraData(userAppDataDir, + nsDependentCString(mAppData->buildID)); + + // see if we have a crashreporter-override.ini in the application directory + nsCOMPtr overrideini; + bool exists; + if (NS_SUCCEEDED(mDirProvider.GetAppDir()->Clone(getter_AddRefs(overrideini))) && + NS_SUCCEEDED(overrideini->AppendNative(NS_LITERAL_CSTRING("crashreporter-override.ini"))) && + NS_SUCCEEDED(overrideini->Exists(&exists)) && + exists) { +#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); + } + } + } +#endif + +#ifdef XP_MACOSX + 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="); + + gRestartArgc = gArgc; + gRestartArgv = (char**) malloc(sizeof(char*) * (gArgc + 1 + (override ? 2 : 0))); + if (!gRestartArgv) { + return 1; + } + + int i; + for (i = 0; i < gArgc; ++i) { + gRestartArgv[i] = gArgv[i]; + } + + // Add the -override argument back (it is removed automatically be CheckArg) if there is one + if (override) { + gRestartArgv[gRestartArgc++] = const_cast("-override"); + gRestartArgv[gRestartArgc++] = const_cast(override); + } + + gRestartArgv[gRestartArgc] = nullptr; + + + if (EnvHasValue("MOZ_SAFE_MODE_RESTART")) { + gSafeMode = true; + // unset the env variable + SaveToEnv("MOZ_SAFE_MODE_RESTART="); + } + + ar = CheckArg("safe-mode", true); + if (ar == ARG_BAD) { + PR_fprintf(PR_STDERR, "Error: argument -safe-mode is invalid when argument -osint is specified\n"); + return 1; + } else if (ar == ARG_FOUND) { + gSafeMode = true; + } + +#ifdef XP_WIN + // If the shift key is pressed and the ctrl and / or alt keys are not pressed + // during startup start in safe mode. GetKeyState returns a short and the high + // order bit will be 1 if the key is pressed. By masking the returned short + // with 0x8000 the result will be 0 if the key is not pressed and non-zero + // otherwise. + if (GetKeyState(VK_SHIFT) & 0x8000 && + !(GetKeyState(VK_CONTROL) & 0x8000) && !(GetKeyState(VK_MENU) & 0x8000)) { + gSafeMode = true; + } +#endif + +#ifdef XP_MACOSX + if (GetCurrentEventKeyModifiers() & optionKey) + gSafeMode = true; +#endif + + // In the Tor Browser, remoting is disabled by default unless -osint is used. + bool allowRemote = (CheckArg("allow-remote") == ARG_FOUND); + if (!allowRemote && (CheckArg("osint", false, nullptr, false) != ARG_FOUND)) + SaveToEnv("MOZ_NO_REMOTE=1"); + + // 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", true); + if (ar == ARG_BAD) { + PR_fprintf(PR_STDERR, "Error: argument -no-remote is invalid when argument -osint is specified\n"); + return 1; + } else if ((ar == ARG_FOUND) && allowRemote) { + PR_fprintf(PR_STDERR, "Error: argument -no-remote is invalid when argument -allow-remote is specified\n"); + return 1; + } + + ar = CheckArg("new-instance", true); + if (ar == ARG_BAD) { + PR_fprintf(PR_STDERR, "Error: argument -new-instance is invalid when argument -osint is specified\n"); + return 1; + } else if (ar == ARG_FOUND) { + SaveToEnv("MOZ_NEW_INSTANCE=1"); + } + + // Handle -help 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; + } + +#ifdef NS_TRACE_MALLOC + gArgc = NS_TraceMallocStartupArgs(gArgc, gArgv); +#endif + + rv = XRE_InitCommandLine(gArgc, gArgv); + NS_ENSURE_SUCCESS(rv, 1); + + // Check for -register, which registers chrome and then exits immediately. + ar = CheckArg("register", true); + if (ar == ARG_BAD) { + PR_fprintf(PR_STDERR, "Error: argument -register is invalid when argument -osint is specified\n"); + return 1; + } else if (ar == ARG_FOUND) { + ScopedXPCOMStartup xpcom; + rv = xpcom.Initialize(); + NS_ENSURE_SUCCESS(rv, 1); + { + nsCOMPtr chromeReg = + mozilla::services::GetChromeRegistryService(); + NS_ENSURE_TRUE(chromeReg, 1); + + chromeReg->CheckForNewChrome(); + } + *aExitFlag = true; + return 0; + } + + if (PR_GetEnv("MOZ_RUN_GTEST")) { + int result; + // RunGTest will only be set if we're in xul-unit + if (mozilla::RunGTest) { + result = mozilla::RunGTest(); + } else { + result = 1; + printf("TEST-UNEXPECTED-FAIL | gtest | Not compiled with enable-tests\n"); + } + *aExitFlag = true; + return result; + } + + return 0; +} + +#ifdef MOZ_CRASHREPORTER +#ifdef XP_WIN +/** + * Uses WMI to read some manufacturer information that may be useful for + * diagnosing hardware-specific crashes. This function is best-effort; failures + * shouldn't burden the caller. COM must be initialized before calling. + */ +static void AnnotateSystemManufacturer() +{ + nsRefPtr locator; + + HRESULT hr = CoCreateInstance(CLSID_WbemLocator, nullptr, CLSCTX_INPROC_SERVER, + IID_IWbemLocator, getter_AddRefs(locator)); + + if (FAILED(hr)) { + return; + } + + nsRefPtr 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; + } + + nsRefPtr enumerator; + + hr = services->ExecQuery(_bstr_t(L"WQL"), _bstr_t(L"SELECT * FROM Win32_BIOS"), + WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY, + nullptr, getter_AddRefs(enumerator)); + + if (FAILED(hr) || !enumerator) { + return; + } + + nsRefPtr classObject; + ULONG results; + + hr = enumerator->Next(WBEM_INFINITE, 1, getter_AddRefs(classObject), &results); + + if (FAILED(hr) || results == 0) { + return; + } + + VARIANT value; + VariantInit(&value); + + hr = classObject->Get(L"Manufacturer", 0, &value, 0, 0); + + if (SUCCEEDED(hr) && V_VT(&value) == VT_BSTR) { + CrashReporter::AnnotateCrashReport(NS_LITERAL_CSTRING("BIOS_Manufacturer"), + NS_ConvertUTF16toUTF8(V_BSTR(&value))); + } + + VariantClear(&value); +} + +static void PR_CALLBACK AnnotateSystemManufacturer_ThreadStart(void*) +{ + HRESULT hr = CoInitialize(nullptr); + + if (FAILED(hr)) { + return; + } + + AnnotateSystemManufacturer(); + + CoUninitialize(); +} +#endif +#endif + +namespace mozilla { + ShutdownChecksMode gShutdownChecks = SCM_NOTHING; +} + +static void SetShutdownChecks() { + // Set default first. On debug builds we crash. On nightly and local + // builds we record. Nightlies will then send the info via telemetry, + // but it is usefull to have the data in about:telemetry in local builds + // too. + +#ifdef DEBUG + gShutdownChecks = SCM_CRASH; +#else + const char* releaseChannel = NS_STRINGIFY(MOZ_UPDATE_CHANNEL); + if (strcmp(releaseChannel, "nightly") == 0 || + strcmp(releaseChannel, "default") == 0) { + gShutdownChecks = SCM_RECORD; + } else { + gShutdownChecks = SCM_NOTHING; + } +#endif + + // We let an environment variable override the default so that addons + // authors can use it for debugging shutdown with released firefox versions. + const char* mozShutdownChecksEnv = PR_GetEnv("MOZ_SHUTDOWN_CHECKS"); + if (mozShutdownChecksEnv) { + if (strcmp(mozShutdownChecksEnv, "crash") == 0) { + gShutdownChecks = SCM_CRASH; + } else if (strcmp(mozShutdownChecksEnv, "record") == 0) { + gShutdownChecks = SCM_RECORD; + } else if (strcmp(mozShutdownChecksEnv, "nothing") == 0) { + gShutdownChecks = SCM_NOTHING; + } + } + +} + +/* + * 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; + + SetShutdownChecks(); + + + // Enable Telemetry IO Reporting on DEBUG, nightly and local builds +#ifdef DEBUG + mozilla::Telemetry::InitIOReporting(gAppData->xreDirectory); +#else + { + const char* releaseChannel = NS_STRINGIFY(MOZ_UPDATE_CHANNEL); + if (strcmp(releaseChannel, "nightly") == 0 || + strcmp(releaseChannel, "default") == 0) { + mozilla::Telemetry::InitIOReporting(gAppData->xreDirectory); + } + } +#endif /* DEBUG */ + +#if defined(MOZ_WIDGET_GTK) || defined(MOZ_ENABLE_XREMOTE) + // 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(MOZ_WIDGET_QT) + nsQAppInstance::AddRef(gArgc, gArgv, true); + + QStringList nonQtArguments = qApp->arguments(); + gQtOnlyArgc = 1; + gQtOnlyArgv = (char**) malloc(sizeof(char*) + * (gRestartArgc - nonQtArguments.size() + 2)); + + // copy binary path + gQtOnlyArgv[0] = gRestartArgv[0]; + + for (int i = 1; i < gRestartArgc; ++i) { + if (!nonQtArguments.contains(gRestartArgv[i])) { + // copy arguments used by Qt for later + gQtOnlyArgv[gQtOnlyArgc++] = gRestartArgv[i]; + } + } + gQtOnlyArgv[gQtOnlyArgc] = nullptr; +#endif +#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. +#if (MOZ_WIDGET_GTK == 2) + if (CheckArg("install")) + gdk_rgb_set_install(TRUE); +#endif + + // 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. + + // 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; + + // display_name is owned by gdk. + const char *display_name = gdk_get_display_arg_name(); + if (display_name) { + SaveWordToEnv("DISPLAY", nsDependentCString(display_name)); + } else { + display_name = PR_GetEnv("DISPLAY"); + if (!display_name) { + PR_fprintf(PR_STDERR, "Error: no display specified\n"); + return 1; + } + } +#endif /* MOZ_WIDGET_GTK2 */ + +#ifdef MOZ_ENABLE_XREMOTE + // handle -remote now that xpcom is fired up + bool newInstance; + { + char *e = PR_GetEnv("MOZ_NO_REMOTE"); + mDisableRemote = (e && *e); + if (mDisableRemote) { + newInstance = true; + } else { + e = PR_GetEnv("MOZ_NEW_INSTANCE"); + newInstance = (e && *e); + } + } + + const char* xremotearg; + ArgResult ar = CheckArg("remote", true, &xremotearg); + if (ar == ARG_BAD) { + PR_fprintf(PR_STDERR, "Error: -remote requires an argument\n"); + return 1; + } + const char* desktopStartupIDPtr = + mDesktopStartupID.IsEmpty() ? nullptr : mDesktopStartupID.get(); + if (ar) { + *aExitFlag = true; + return HandleRemoteArgument(xremotearg, desktopStartupIDPtr); + } + + if (!newInstance) { + // Try to remote the entire command line. If this fails, start up normally. + RemoteResult rr = RemoteCommandLine(desktopStartupIDPtr); + if (rr == REMOTE_FOUND) { + *aExitFlag = true; + return 0; + } + else if (rr == REMOTE_ARG_BAD) + return 1; + } +#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. + // This is done only on X11 platforms if the environment variable MOZ_USE_OMTC is set so + // as to avoid overhead when omtc is not used. + // + // On nightly builds, we call this by default to enable OMTC for Electrolysis testing. On + // aurora, beta, and release builds, there is a small tpaint regression from enabling this + // call, so it sits behind an environment variable. + // + // An environment variable is used instead of a pref on X11 platforms because we start having + // access to prefs long after the first call to XOpenDisplay which is hard to change due to + // interdependencies in the initialization. +# ifndef NIGHTLY_BUILD + if (PR_GetEnv("MOZ_USE_OMTC") || + PR_GetEnv("MOZ_OMTC_ENABLED")) +# endif + { + XInitThreads(); + } +#endif +#if defined(MOZ_WIDGET_GTK) + 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); + + // g_set_application_name () is only defined in glib2.2 and higher. + _g_set_application_name_fn _g_set_application_name = + (_g_set_application_name_fn)FindFunction("g_set_application_name"); + if (_g_set_application_name) { + _g_set_application_name(mAppData->name); + } + _gtk_window_set_auto_startup_notification_fn _gtk_window_set_auto_startup_notification = + (_gtk_window_set_auto_startup_notification_fn)FindFunction("gtk_window_set_auto_startup_notification"); + if (_gtk_window_set_auto_startup_notification) { + _gtk_window_set_auto_startup_notification(false); + } + +#if (MOZ_WIDGET_GTK == 2) + gtk_widget_set_default_colormap(gdk_rgb_get_colormap()); +#endif /* (MOZ_WIDGET_GTK == 2) */ +#endif /* defined(MOZ_WIDGET_GTK) */ +#ifdef MOZ_X11 + // Do this after initializing GDK, or GDK will install its own handler. + 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 + +#if defined(USE_MOZ_UPDATER) + // Check for and process any available updates + nsCOMPtr updRoot; + bool persistent; + rv = mDirProvider.GetFile(XRE_UPDATE_ROOT_DIR, &persistent, + getter_AddRefs(updRoot)); + // XRE_UPDATE_ROOT_DIR may fail. Fallback to appDir if failed + if (NS_FAILED(rv)) + updRoot = mDirProvider.GetAppDir(); + + // If the MOZ_PROCESS_UPDATES environment variable already exists, then + // we are being called from the callback application. + if (EnvHasValue("MOZ_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", false, &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_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("process-updates")) { + SaveToEnv("MOZ_PROCESS_UPDATES=1"); + } + nsCOMPtr exeFile, exeDir; + rv = mDirProvider.GetFile(XRE_EXECUTABLE_FILE, &persistent, + getter_AddRefs(exeFile)); + NS_ENSURE_SUCCESS(rv, 1); + rv = exeFile->GetParent(getter_AddRefs(exeDir)); + NS_ENSURE_SUCCESS(rv, 1); +#ifdef TOR_BROWSER_UPDATE + nsAutoCString compatVersion(TOR_BROWSER_VERSION); +#endif + ProcessUpdates(mDirProvider.GetGREDir(), + exeDir, + updRoot, + gRestartArgc, + gRestartArgv, +#ifdef TOR_BROWSER_UPDATE + compatVersion.get() +#else + mAppData->version +#endif + ); + if (EnvHasValue("MOZ_PROCESS_UPDATES")) { + SaveToEnv("MOZ_PROCESS_UPDATES="); + *aExitFlag = true; + return 0; + } +#if defined(XP_WIN) && defined(MOZ_METRO) + if (CheckArg("metro-update", false) == ARG_FOUND) { + *aExitFlag = true; + return 0; + } +#endif +#endif + + 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; + } + + rv = SelectProfile(getter_AddRefs(mProfileLock), mProfileSvc, mNativeApp, &mStartOffline, + &mProfileName); + 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; + } + gProfileLock = mProfileLock; + + rv = mProfileLock->GetDirectory(getter_AddRefs(mProfD)); + NS_ENSURE_SUCCESS(rv, 1); + + rv = mProfileLock->GetLocalDirectory(getter_AddRefs(mProfLD)); + NS_ENSURE_SUCCESS(rv, 1); + + rv = mDirProvider.SetProfile(mProfD, mProfLD); + NS_ENSURE_SUCCESS(rv, 1); + + //////////////////////// NOW WE HAVE A PROFILE //////////////////////// + + mozilla::Telemetry::SetProfileDir(mProfD); + +#ifdef MOZ_CRASHREPORTER + if (mAppData->flags & NS_XRE_ENABLE_CRASH_REPORTER) + MakeOrSetMinidumpPath(mProfD); + + CrashReporter::UpdateCrashEventsDir(); +#endif + + nsAutoCString version; + BuildVersion(version); + +#ifdef TARGET_OS_ABI + NS_NAMED_LITERAL_CSTRING(osABI, TARGET_OS_ABI); +#else + // No TARGET_XPCOM_ABI, but at least the OS is known + NS_NAMED_LITERAL_CSTRING(osABI, 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 extension upgrade or installation. + + // If we see .purgecaches, that means someone did a make. + // Re-register components to catch potential changes. + nsCOMPtr flagFile; + + rv = NS_ERROR_FILE_NOT_FOUND; + nsCOMPtr fFlagFile; + if (mAppData->directory) { + rv = mAppData->directory->Clone(getter_AddRefs(fFlagFile)); + } + flagFile = do_QueryInterface(fFlagFile); + if (flagFile) { + flagFile->AppendNative(FILE_INVALIDATE_CACHES); + } + + bool cachesOK; + bool versionOK = CheckCompatibility(mProfD, version, osABI, + mDirProvider.GetGREDir(), + mAppData->directory, flagFile, + &cachesOK); + if (CheckArg("purgecaches")) { + cachesOK = false; + } + if (PR_GetEnv("MOZ_PURGE_CACHES")) { + 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 (gSafeMode) { + startupCacheValid = RemoveComponentRegistries(mProfD, mProfLD, false); + WriteVersion(mProfD, NS_LITERAL_CSTRING("Safe Mode"), osABI, + mDirProvider.GetGREDir(), mAppData->directory, !startupCacheValid); + } + else if (versionOK) { + if (!cachesOK) { + // Remove caches, forcing component re-registration. + // The new list of additional components directories is derived from + // information in "extensions.ini". + startupCacheValid = RemoveComponentRegistries(mProfD, mProfLD, false); + + // Rewrite compatibility.ini to remove the flag + WriteVersion(mProfD, version, osABI, + mDirProvider.GetGREDir(), mAppData->directory, !startupCacheValid); + } + // Nothing need be done for the normal startup case. + } + else { + // Remove caches, forcing component re-registration + // with the default set of components (this disables any potentially + // troublesome incompatible XPCOM components). + startupCacheValid = RemoveComponentRegistries(mProfD, mProfLD, true); + + // Write out version + WriteVersion(mProfD, version, osABI, + mDirProvider.GetGREDir(), mAppData->directory, !startupCacheValid); + } + + if (!startupCacheValid) + StartupCache::IgnoreDiskCache(); + + if (flagFile) { + flagFile->Remove(true); + } + + return 0; +} + +/* + * 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."); + +#ifdef NS_FUNCTION_TIMER + // initialize some common services, so we don't pay the cost for these at odd times later on; + // SetWindowCreator -> ChromeRegistry -> IOService -> SocketTransportService -> (nspr wspm init), Prefs + { + nsCOMPtr comp; + + comp = do_GetService("@mozilla.org/preferences-service;1"); + + comp = do_GetService("@mozilla.org/network/socket-transport-service;1"); + + comp = do_GetService("@mozilla.org/network/dns-service;1"); + + comp = do_GetService("@mozilla.org/network/io-service;1"); + + comp = do_GetService("@mozilla.org/chrome/chrome-registry;1"); + + comp = do_GetService("@mozilla.org/focus-event-suppressor-service;1"); + } +#endif + + rv = mScopedXPCom->SetWindowCreator(mNativeApp); + NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE); + +#ifdef MOZ_CRASHREPORTER + // tell the crash reporter to also send the release channel + nsCOMPtr prefs = do_GetService("@mozilla.org/preferences-service;1", &rv); + if (NS_SUCCEEDED(rv)) { + nsCOMPtr defaultPrefBranch; + rv = prefs->GetDefaultBranch(nullptr, getter_AddRefs(defaultPrefBranch)); + + if (NS_SUCCEEDED(rv)) { + nsXPIDLCString sval; + rv = defaultPrefBranch->GetCharPref("app.update.channel", getter_Copies(sval)); + if (NS_SUCCEEDED(rv)) { + CrashReporter::AnnotateCrashReport(NS_LITERAL_CSTRING("ReleaseChannel"), + sval); + } + } + } + // Needs to be set after xpcom initialization. + CrashReporter::AnnotateCrashReport(NS_LITERAL_CSTRING("FramePoisonBase"), + nsPrintfCString("%.16llx", uint64_t(gMozillaPoisonBase))); + CrashReporter::AnnotateCrashReport(NS_LITERAL_CSTRING("FramePoisonSize"), + nsPrintfCString("%lu", uint32_t(gMozillaPoisonSize))); + +#ifdef XP_WIN + PR_CreateThread(PR_USER_THREAD, AnnotateSystemManufacturer_ThreadStart, 0, + PR_PRIORITY_LOW, PR_GLOBAL_THREAD, PR_UNJOINABLE_THREAD, 0); +#endif + +#endif + + if (mStartOffline) { + nsCOMPtr io (do_GetService("@mozilla.org/network/io-service;1")); + NS_ENSURE_TRUE(io, NS_ERROR_FAILURE); + io->SetManageOfflineStatus(false); + io->SetOffline(true); + } + + { + nsCOMPtr startupNotifier + (do_CreateInstance(NS_APPSTARTUPNOTIFIER_CONTRACTID, &rv)); + NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE); + + startupNotifier->Observe(nullptr, APPSTARTUP_TOPIC, nullptr); + } + + nsCOMPtr appStartup + (do_GetService(NS_APPSTARTUP_CONTRACTID)); + NS_ENSURE_TRUE(appStartup, NS_ERROR_FAILURE); + + if (gDoMigration) { + nsCOMPtr file; + mDirProvider.GetAppDir()->Clone(getter_AddRefs(file)); + file->AppendNative(NS_LITERAL_CSTRING("override.ini")); + nsINIParser parser; + nsresult 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; + } + } + } + } + + { + nsCOMPtr selectedProfile; + if (gDoProfileReset) { + // At this point we can be sure that profile reset is happening on the default profile. + rv = mProfileSvc->GetSelectedProfile(getter_AddRefs(selectedProfile)); + if (NS_FAILED(rv)) { + gDoProfileReset = false; + return NS_ERROR_FAILURE; + } + } + + // Profile Migration + if (mAppData->flags & NS_XRE_ENABLE_PROFILE_MIGRATOR && gDoMigration) { + gDoMigration = false; + nsCOMPtr pm(do_CreateInstance(NS_PROFILEMIGRATOR_CONTRACTID)); + if (pm) { + nsAutoCString aKey; + if (gDoProfileReset) { + // Automatically migrate from the current application if we just + // reset the profile. + aKey = MOZ_APP_NAME; + } + pm->Migrate(&mDirProvider, aKey); + } + } + + if (gDoProfileReset) { + nsresult backupCreated = ProfileResetCleanup(selectedProfile); + if (NS_FAILED(backupCreated)) NS_WARNING("Could not cleanup the profile that was reset"); + + // Set the new profile as the default after we're done cleaning up the old default. + rv = SetCurrentProfileAsDefault(mProfileSvc, mProfD); + if (NS_FAILED(rv)) NS_WARNING("Could not set current profile as the default"); + } + } + + mDirProvider.DoStartup(); + +#ifdef MOZ_CRASHREPORTER + nsCString userAgentLocale; + if (NS_SUCCEEDED(Preferences::GetCString("general.useragent.locale", &userAgentLocale))) { + CrashReporter::AnnotateCrashReport(NS_LITERAL_CSTRING("useragent_locale"), userAgentLocale); + } +#endif + + appStartup->GetShuttingDown(&mShuttingDown); + + nsCOMPtr cmdLine; + + nsCOMPtr workingDir; + rv = NS_GetSpecialDirectory(NS_OS_CURRENT_WORKING_DIR, getter_AddRefs(workingDir)); + NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE); + + if (!mShuttingDown) { + cmdLine = do_CreateInstance("@mozilla.org/toolkit/command-line;1"); + NS_ENSURE_TRUE(cmdLine, NS_ERROR_FAILURE); + + 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 obsService = + mozilla::services::GetObserverService(); + if (obsService) { + obsService->NotifyObservers(cmdLine, "command-line-startup", nullptr); + } + } + + SaveStateForAppInitiatedRestart(); + + // 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_PROFILE_NAME="); + SaveToEnv("XRE_START_OFFLINE="); + SaveToEnv("NO_EM_RESTART="); + SaveToEnv("XUL_APP_FILE="); + SaveToEnv("XRE_BINARY_PATH="); + + if (!mShuttingDown) { + rv = appStartup->CreateHiddenWindow(); + NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE); + +#if defined(HAVE_DESKTOP_STARTUP_ID) && defined(MOZ_WIDGET_GTK) + nsGTKToolkit* toolkit = nsGTKToolkit::GetToolkit(); + if (toolkit && !mDesktopStartupID.IsEmpty()) { + toolkit->SetDesktopStartupID(mDesktopStartupID); + } + // 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 + // Set up ability to respond to system (Apple) events. This must be + // done before setting up the command line service. + SetupMacApplicationDelegate(); + + // we re-initialize the command-line service and do appleevents munging + // after we are sure that we're not restarting + cmdLine = do_CreateInstance("@mozilla.org/toolkit/command-line;1"); + NS_ENSURE_TRUE(cmdLine, NS_ERROR_FAILURE); + + CommandLineServiceMac::SetupMacCommandLine(gArgc, gArgv, false); + + rv = cmdLine->Init(gArgc, gArgv, + workingDir, nsICommandLine::STATE_INITIAL_LAUNCH); + NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE); +#endif + + nsCOMPtr obsService = + mozilla::services::GetObserverService(); + if (obsService) + obsService->NotifyObservers(nullptr, "final-ui-startup", nullptr); + + (void)appStartup->DoneStartingUp(); + appStartup->GetShuttingDown(&mShuttingDown); + } + + if (!mShuttingDown) { + rv = cmdLine->Run(); + NS_ENSURE_SUCCESS_LOG(rv, NS_ERROR_FAILURE); + + appStartup->GetShuttingDown(&mShuttingDown); + } + + if (!mShuttingDown) { +#ifdef MOZ_ENABLE_XREMOTE + // if we have X remote support, start listening for requests on the + // proxy window. + if (!mDisableRemote) + mRemoteService = do_GetService("@mozilla.org/toolkit/remote-service;1"); + if (mRemoteService) + mRemoteService->Startup(mAppData->name, mProfileName.get()); +#endif /* MOZ_ENABLE_XREMOTE */ + + mNativeApp->Enable(); + } + +#ifdef MOZ_INSTRUMENT_EVENT_LOOP + if (PR_GetEnv("MOZ_INSTRUMENT_EVENT_LOOP") || profiler_is_active()) { + bool logToConsole = !!PR_GetEnv("MOZ_INSTRUMENT_EVENT_LOOP"); + mozilla::InitEventTracing(logToConsole); + } +#endif /* MOZ_INSTRUMENT_EVENT_LOOP */ + + { + rv = appStartup->Run(); + if (NS_FAILED(rv)) { + NS_ERROR("failed to run appstartup"); + gLogConsoleErrors = true; + } + } + + return rv; +} + +/* + * XRE_main - A class based main entry point used by most platforms. + */ +int +XREMain::XRE_main(int argc, char* argv[], const nsXREAppData* aAppData) +{ + char aLocal; + GeckoProfilerInitRAII profilerGuard(&aLocal); + PROFILER_LABEL("Startup", "XRE_Main"); + + mozilla::IOInterposerInit ioInterposerGuard; + + nsresult rv = NS_OK; + + gArgc = argc; + gArgv = argv; + + NS_ENSURE_TRUE(aAppData, 2); + + mAppData = new ScopedAppData(aAppData); + if (!mAppData) + return 1; + // used throughout this file + gAppData = mAppData; + + ScopedLogging log; + +#if defined(MOZ_WIDGET_GTK) +#if defined(MOZ_MEMORY) || defined(__FreeBSD__) || defined(__NetBSD__) + // Disable the slice allocator, since jemalloc already uses similar layout + // algorithms, and using a sub-allocator tends to increase fragmentation. + // This must be done before g_thread_init() is called. + g_slice_set_config(G_SLICE_CONFIG_ALWAYS_MALLOC, 1); +#endif + g_thread_init(nullptr); +#endif + + // init + bool exit = false; + int result = XRE_mainInit(&exit); + if (result != 0 || exit) + return result; + + // startup + result = XRE_mainStartup(&exit); + if (result != 0 || exit) + return result; + + bool appInitiatedRestart = false; + + // Start the real application + mScopedXPCom = new ScopedXPCOMStartup(); + if (!mScopedXPCom) + return 1; + + rv = mScopedXPCom->Initialize(); + NS_ENSURE_SUCCESS(rv, 1); + + // run! + rv = XRE_mainRun(); + +#ifdef MOZ_INSTRUMENT_EVENT_LOOP + mozilla::ShutdownEventTracing(); +#endif + + // Check for an application initiated restart. This is one that + // corresponds to nsIAppStartup.quit(eRestart) + if (rv == NS_SUCCESS_RESTART_APP || rv == NS_SUCCESS_RESTART_METRO_APP) { + appInitiatedRestart = true; + + // We have an application restart don't do any shutdown checks here + // In particular we don't want to poison IO for checking late-writes. + gShutdownChecks = SCM_NOTHING; + } + + if (!mShuttingDown) { +#ifdef MOZ_ENABLE_XREMOTE + // shut down the x remote proxy window + if (mRemoteService) { + mRemoteService->Shutdown(); + } +#endif /* MOZ_ENABLE_XREMOTE */ + } + + delete mScopedXPCom; + mScopedXPCom = nullptr; + + // unlock the profile after ScopedXPCOMStartup object (xpcom) + // has gone out of scope. see bug #386739 for more details + mProfileLock->Unlock(); + gProfileLock = nullptr; + +#if defined(MOZ_WIDGET_QT) + nsQAppInstance::Release(); +#endif + + // Restart the app after XPCOM has been shut down cleanly. + if (appInitiatedRestart) { + RestoreStateForAppInitiatedRestart(); + + // Ensure that these environment variables are set: + SaveFileToEnvIfUnset("XRE_PROFILE_PATH", mProfD); + SaveFileToEnvIfUnset("XRE_PROFILE_LOCAL_PATH", mProfLD); + SaveWordToEnvIfUnset("XRE_PROFILE_NAME", mProfileName); + +#ifdef MOZ_WIDGET_GTK + MOZ_gdk_display_close(mGdkDisplay); +#endif + +#if defined(MOZ_METRO) && defined(XP_WIN) + if (rv == NS_SUCCESS_RESTART_METRO_APP) { + LaunchDefaultMetroBrowser(); + rv = NS_OK; + } else +#endif + { + rv = LaunchChild(mNativeApp, true); + } + +#ifdef MOZ_CRASHREPORTER + if (mAppData->flags & NS_XRE_ENABLE_CRASH_REPORTER) + CrashReporter::UnsetExceptionHandler(); +#endif + return rv == NS_ERROR_LAUNCHED_CHILD_PROCESS ? 0 : 1; + } + +#ifdef MOZ_WIDGET_GTK + // gdk_display_close also calls gdk_display_manager_set_default_display + // appropriately when necessary. + MOZ_gdk_display_close(mGdkDisplay); +#endif + +#ifdef MOZ_CRASHREPORTER + if (mAppData->flags & NS_XRE_ENABLE_CRASH_REPORTER) + CrashReporter::UnsetExceptionHandler(); +#endif + + XRE_DeinitCommandLine(); + + return NS_FAILED(rv) ? 1 : 0; +} + +#if defined(MOZ_METRO) && defined(XP_WIN) +extern bool XRE_MetroCoreApplicationRun(); +static XREMain* xreMainPtr; + +// must be called by the thread we want as the main thread +nsresult +XRE_metroStartup(bool runXREMain) +{ + nsresult rv; + + bool exit = false; + if (xreMainPtr->XRE_mainStartup(&exit) != 0 || exit) + return NS_ERROR_FAILURE; + + // Start the real application + xreMainPtr->mScopedXPCom = new ScopedXPCOMStartup(); + if (!xreMainPtr->mScopedXPCom) + return NS_ERROR_FAILURE; + + rv = xreMainPtr->mScopedXPCom->Initialize(); + NS_ENSURE_SUCCESS(rv, rv); + + if (runXREMain) { + rv = xreMainPtr->XRE_mainRun(); + NS_ENSURE_SUCCESS(rv, rv); + } + return NS_OK; +} + +void +XRE_metroShutdown() +{ + delete xreMainPtr->mScopedXPCom; + xreMainPtr->mScopedXPCom = nullptr; + +#ifdef MOZ_INSTRUMENT_EVENT_LOOP + mozilla::ShutdownEventTracing(); +#endif + + // unlock the profile after ScopedXPCOMStartup object (xpcom) + // has gone out of scope. see bug #386739 for more details + xreMainPtr->mProfileLock->Unlock(); + gProfileLock = nullptr; + +#ifdef MOZ_CRASHREPORTER + if (xreMainPtr->mAppData->flags & NS_XRE_ENABLE_CRASH_REPORTER) + CrashReporter::UnsetExceptionHandler(); +#endif + + XRE_DeinitCommandLine(); +} + +class WinRTInitWrapper +{ +public: + WinRTInitWrapper() { + mResult = ::RoInitialize(RO_INIT_MULTITHREADED); + } + ~WinRTInitWrapper() { + if (SUCCEEDED(mResult)) { + ::RoUninitialize(); + } + } + HRESULT mResult; +}; + +int +XRE_mainMetro(int argc, char* argv[], const nsXREAppData* aAppData) +{ + char aLocal; + GeckoProfilerInitRAII profilerGuard(&aLocal); + PROFILER_LABEL("Startup", "XRE_Main"); + + mozilla::IOInterposerInit ioInterposerGuard; + + nsresult rv = NS_OK; + + xreMainPtr = new XREMain(); + if (!xreMainPtr) { + return 1; + } + + // Inits Winrt and COM underneath it. + WinRTInitWrapper wrap; + + gArgc = argc; + gArgv = argv; + + NS_ENSURE_TRUE(aAppData, 2); + + xreMainPtr->mAppData = new ScopedAppData(aAppData); + if (!xreMainPtr->mAppData) + return 1; + // used throughout this file + gAppData = xreMainPtr->mAppData; + + ScopedLogging log; + + // init + bool exit = false; + if (xreMainPtr->XRE_mainInit(&exit) != 0 || exit) + return 1; + + // Located in widget, will call back into XRE_metroStartup and + // XRE_metroShutdown above. + if (!XRE_MetroCoreApplicationRun()) { + return 1; + } + + // XRE_metroShutdown should have already been called on the worker + // thread that called XRE_metroStartup. + NS_ASSERTION(!xreMainPtr->mScopedXPCom, + "XPCOM Shutdown hasn't occured, and we are exiting."); + return 0; +} + +void SetWindowsEnvironment(WindowsEnvironmentType aEnvID); +#endif // MOZ_METRO || !defined(XP_WIN) + +void +XRE_StopLateWriteChecks(void) { + mozilla::StopLateWriteChecks(); +} + +int +XRE_main(int argc, char* argv[], const nsXREAppData* aAppData, uint32_t aFlags) +{ +#if !defined(MOZ_METRO) || !defined(XP_WIN) + XREMain main; + int result = main.XRE_main(argc, argv, aAppData); + mozilla::RecordShutdownEndTimeStamp(); + return result; +#else + if (aFlags == XRE_MAIN_FLAG_USE_METRO) { + SetWindowsEnvironment(WindowsEnvironmentType_Metro); + } + + // Desktop + if (XRE_GetWindowsEnvironment() == WindowsEnvironmentType_Desktop) { + XREMain main; + int result = main.XRE_main(argc, argv, aAppData); + mozilla::RecordShutdownEndTimeStamp(); + return result; + } + + // Metro + NS_ASSERTION(XRE_GetWindowsEnvironment() == WindowsEnvironmentType_Metro, + "Unknown Windows environment"); + + SetLastWinRunType(AHE_IMMERSIVE); + + int result = XRE_mainMetro(argc, argv, aAppData); + mozilla::RecordShutdownEndTimeStamp(); + return result; +#endif // MOZ_METRO || !defined(XP_WIN) +} + +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 binFile; + rv = XRE_GetBinaryPath(aArgv[0], 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 + + const char *path = nullptr; + ArgResult ar = CheckArg("greomni", false, &path); + if (ar == ARG_BAD) { + PR_fprintf(PR_STDERR, "Error: argument -greomni requires a path argument\n"); + return NS_ERROR_FAILURE; + } + + if (!path) + return rv; + + nsCOMPtr greOmni; + rv = XRE_GetFileFromPath(path, getter_AddRefs(greOmni)); + if (NS_FAILED(rv)) { + PR_fprintf(PR_STDERR, "Error: argument -greomni requires a valid path\n"); + return rv; + } + + ar = CheckArg("appomni", false, &path); + if (ar == ARG_BAD) { + PR_fprintf(PR_STDERR, "Error: argument -appomni requires a path argument\n"); + return NS_ERROR_FAILURE; + } + + nsCOMPtr appOmni; + if (path) { + rv = XRE_GetFileFromPath(path, getter_AddRefs(appOmni)); + if (NS_FAILED(rv)) { + PR_fprintf(PR_STDERR, "Error: argument -appomni requires a valid path\n"); + return rv; + } + } + + mozilla::Omnijar::Init(greOmni, appOmni); + return rv; +} + +nsresult +XRE_DeinitCommandLine() +{ + nsresult rv = NS_OK; + + CommandLine::Terminate(); + + return rv; +} + +GeckoProcessType +XRE_GetProcessType() +{ + return mozilla::startup::sChildProcessType; +} + +bool +mozilla::BrowserTabsRemote() +{ + if (!gBrowserTabsRemoteInitialized) { + gBrowserTabsRemote = Preferences::GetBool("browser.tabs.remote", false); + gBrowserTabsRemoteInitialized = true; + } + + return gBrowserTabsRemote; +} + +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_WIN32 + // 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 + +#if defined (DEBUG) && defined(XP_WIN) + // Send MSCRT Warnings, Errors and Assertions to stderr. + // See http://msdn.microsoft.com/en-us/library/1y71x448(v=VS.80).aspx + // and http://msdn.microsoft.com/en-us/library/a68f826y(v=VS.80).aspx. + + _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE); + _CrtSetReportFile(_CRT_WARN, _CRTDBG_FILE_STDERR); + _CrtSetReportMode(_CRT_ERROR, _CRTDBG_MODE_FILE); + _CrtSetReportFile(_CRT_ERROR, _CRTDBG_FILE_STDERR); + _CrtSetReportMode(_CRT_ASSERT, _CRTDBG_MODE_FILE); + _CrtSetReportFile(_CRT_ASSERT, _CRTDBG_FILE_STDERR); + + _CrtSetReportHook(MSCRTReportHook); +#endif + + InstallSignalHandlers(progname); + + // Unbuffer stdout, needed for tinderbox tests. + setbuf(stdout, 0); +} +