michael@0: /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #if defined(MOZ_WIDGET_QT) michael@0: #include michael@0: #include michael@0: #include "nsQAppInstance.h" michael@0: #endif // MOZ_WIDGET_QT michael@0: michael@0: #include "mozilla/dom/ContentParent.h" michael@0: #include "mozilla/dom/ContentChild.h" michael@0: michael@0: #include "mozilla/ArrayUtils.h" michael@0: #include "mozilla/Attributes.h" michael@0: #include "mozilla/IOInterposer.h" michael@0: #include "mozilla/Likely.h" michael@0: #include "mozilla/Poison.h" michael@0: #include "mozilla/Preferences.h" michael@0: #include "mozilla/Telemetry.h" michael@0: michael@0: #include "nsAppRunner.h" michael@0: #include "mozilla/AppData.h" michael@0: #include "nsUpdateDriver.h" michael@0: #include "ProfileReset.h" michael@0: michael@0: #ifdef MOZ_INSTRUMENT_EVENT_LOOP michael@0: #include "EventTracer.h" michael@0: #endif michael@0: michael@0: #ifdef XP_MACOSX michael@0: #include "nsVersionComparator.h" michael@0: #include "MacLaunchHelper.h" michael@0: #include "MacApplicationDelegate.h" michael@0: #include "MacAutoreleasePool.h" michael@0: // these are needed for sysctl michael@0: #include michael@0: #include michael@0: #endif michael@0: michael@0: #include "prmem.h" michael@0: #include "prnetdb.h" michael@0: #include "prprf.h" michael@0: #include "prproces.h" michael@0: #include "prenv.h" michael@0: michael@0: #include "nsIAppShellService.h" michael@0: #include "nsIAppStartup.h" michael@0: #include "nsIAppStartupNotifier.h" michael@0: #include "nsIMutableArray.h" michael@0: #include "nsICategoryManager.h" michael@0: #include "nsIChromeRegistry.h" michael@0: #include "nsICommandLineRunner.h" michael@0: #include "nsIComponentManager.h" michael@0: #include "nsIComponentRegistrar.h" michael@0: #include "nsIContentHandler.h" michael@0: #include "nsIDialogParamBlock.h" michael@0: #include "nsIDOMWindow.h" michael@0: #include "mozilla/ModuleUtils.h" michael@0: #include "nsIIOService2.h" michael@0: #include "nsIObserverService.h" michael@0: #include "nsINativeAppSupport.h" michael@0: #include "nsIProcess.h" michael@0: #include "nsIProfileUnlocker.h" michael@0: #include "nsIPromptService.h" michael@0: #include "nsIServiceManager.h" michael@0: #include "nsIStringBundle.h" michael@0: #include "nsISupportsPrimitives.h" michael@0: #include "nsIToolkitChromeRegistry.h" michael@0: #include "nsIToolkitProfile.h" michael@0: #include "nsIToolkitProfileService.h" michael@0: #include "nsIURI.h" michael@0: #include "nsIWindowCreator.h" michael@0: #include "nsIWindowMediator.h" michael@0: #include "nsIWindowWatcher.h" michael@0: #include "nsIXULAppInfo.h" michael@0: #include "nsIXULRuntime.h" michael@0: #include "nsPIDOMWindow.h" michael@0: #include "nsIBaseWindow.h" michael@0: #include "nsIWidget.h" michael@0: #include "nsIDocShell.h" michael@0: #include "nsAppShellCID.h" michael@0: #include "mozilla/scache/StartupCache.h" michael@0: michael@0: #include "mozilla/unused.h" michael@0: michael@0: #ifdef XP_WIN michael@0: #include "nsIWinAppHelper.h" michael@0: #include michael@0: #include "cairo/cairo-features.h" michael@0: #include "mozilla/WindowsVersion.h" michael@0: #ifdef MOZ_METRO michael@0: #include michael@0: #endif michael@0: michael@0: #ifndef PROCESS_DEP_ENABLE michael@0: #define PROCESS_DEP_ENABLE 0x1 michael@0: #endif michael@0: #endif michael@0: michael@0: #include "nsCRT.h" michael@0: #include "nsCOMPtr.h" michael@0: #include "nsDirectoryServiceDefs.h" michael@0: #include "nsDirectoryServiceUtils.h" michael@0: #include "nsEmbedCID.h" michael@0: #include "nsNetUtil.h" michael@0: #include "nsReadableUtils.h" michael@0: #include "nsXPCOM.h" michael@0: #include "nsXPCOMCIDInternal.h" michael@0: #include "nsXPIDLString.h" michael@0: #include "nsPrintfCString.h" michael@0: #include "nsVersionComparator.h" michael@0: michael@0: #include "nsAppDirectoryServiceDefs.h" michael@0: #include "nsXULAppAPI.h" michael@0: #include "nsXREDirProvider.h" michael@0: #include "nsToolkitCompsCID.h" michael@0: michael@0: #if defined(XP_WIN) && defined(MOZ_METRO) michael@0: #include "updatehelper.h" michael@0: #endif michael@0: michael@0: #include "nsINIParser.h" michael@0: #include "mozilla/Omnijar.h" michael@0: #include "mozilla/StartupTimeline.h" michael@0: #include "mozilla/LateWriteChecks.h" michael@0: michael@0: #include michael@0: michael@0: #ifdef XP_UNIX michael@0: #include michael@0: #include michael@0: #include michael@0: #endif michael@0: michael@0: #ifdef XP_WIN michael@0: #include michael@0: #include michael@0: #include "nsThreadUtils.h" michael@0: #include michael@0: #include michael@0: #endif michael@0: michael@0: #ifdef XP_MACOSX michael@0: #include "nsILocalFileMac.h" michael@0: #include "nsCommandLineServiceMac.h" michael@0: #endif michael@0: michael@0: // for X remote support michael@0: #ifdef MOZ_ENABLE_XREMOTE michael@0: #include "XRemoteClient.h" michael@0: #include "nsIRemoteService.h" michael@0: #endif michael@0: michael@0: #ifdef NS_TRACE_MALLOC michael@0: #include "nsTraceMalloc.h" michael@0: #endif michael@0: michael@0: #if defined(DEBUG) && defined(XP_WIN32) michael@0: #include michael@0: #endif michael@0: michael@0: #if defined (XP_MACOSX) michael@0: #include michael@0: #endif michael@0: michael@0: #ifdef DEBUG michael@0: #include "prlog.h" michael@0: #endif michael@0: michael@0: #ifdef MOZ_JPROF michael@0: #include "jprof.h" michael@0: #endif michael@0: michael@0: #ifdef MOZ_CRASHREPORTER michael@0: #include "nsExceptionHandler.h" michael@0: #include "nsICrashReporter.h" michael@0: #define NS_CRASHREPORTER_CONTRACTID "@mozilla.org/toolkit/crash-reporter;1" michael@0: #include "nsIPrefService.h" michael@0: #endif michael@0: michael@0: #include "base/command_line.h" michael@0: #ifdef MOZ_ENABLE_TESTS michael@0: #include "GTestRunner.h" michael@0: #endif michael@0: michael@0: #ifdef MOZ_WIDGET_ANDROID michael@0: #include "AndroidBridge.h" michael@0: #endif michael@0: michael@0: extern uint32_t gRestartMode; michael@0: extern void InstallSignalHandlers(const char *ProgramName); michael@0: #include "nsX11ErrorHandler.h" michael@0: michael@0: #define FILE_COMPATIBILITY_INFO NS_LITERAL_CSTRING("compatibility.ini") michael@0: #define FILE_INVALIDATE_CACHES NS_LITERAL_CSTRING(".purgecaches") michael@0: michael@0: int gArgc; michael@0: char **gArgv; michael@0: michael@0: static const char gToolkitVersion[] = NS_STRINGIFY(GRE_MILESTONE); michael@0: static const char gToolkitBuildID[] = NS_STRINGIFY(GRE_BUILDID); michael@0: michael@0: static nsIProfileLock* gProfileLock; michael@0: michael@0: int gRestartArgc; michael@0: char **gRestartArgv; michael@0: michael@0: #ifdef MOZ_WIDGET_QT michael@0: static int gQtOnlyArgc; michael@0: static char **gQtOnlyArgv; michael@0: #endif michael@0: michael@0: #if defined(MOZ_WIDGET_GTK) michael@0: #if defined(DEBUG) || defined(NS_BUILD_REFCNT_LOGGING) \ michael@0: || defined(NS_TRACE_MALLOC) michael@0: #define CLEANUP_MEMORY 1 michael@0: #define PANGO_ENABLE_BACKEND michael@0: #include michael@0: #endif michael@0: #include michael@0: #ifdef MOZ_X11 michael@0: #include michael@0: #endif /* MOZ_X11 */ michael@0: #include "nsGTKToolkit.h" michael@0: #include michael@0: #endif michael@0: #include "BinaryPath.h" michael@0: michael@0: #ifdef MOZ_LINKER michael@0: extern "C" MFBT_API bool IsSignalHandlingBroken(); michael@0: #endif michael@0: michael@0: namespace mozilla { michael@0: int (*RunGTest)() = 0; michael@0: } michael@0: michael@0: using namespace mozilla; michael@0: using mozilla::unused; michael@0: using mozilla::scache::StartupCache; michael@0: using mozilla::dom::ContentParent; michael@0: using mozilla::dom::ContentChild; michael@0: michael@0: // Save literal putenv string to environment variable. michael@0: static void michael@0: SaveToEnv(const char *putenv) michael@0: { michael@0: char *expr = strdup(putenv); michael@0: if (expr) michael@0: PR_SetEnv(expr); michael@0: // We intentionally leak |expr| here since it is required by PR_SetEnv. michael@0: } michael@0: michael@0: // Tests that an environment variable exists and has a value michael@0: static bool michael@0: EnvHasValue(const char *name) michael@0: { michael@0: const char *val = PR_GetEnv(name); michael@0: return (val && *val); michael@0: } michael@0: michael@0: // Save the given word to the specified environment variable. michael@0: static void michael@0: SaveWordToEnv(const char *name, const nsACString & word) michael@0: { michael@0: char *expr = PR_smprintf("%s=%s", name, PromiseFlatCString(word).get()); michael@0: if (expr) michael@0: PR_SetEnv(expr); michael@0: // We intentionally leak |expr| here since it is required by PR_SetEnv. michael@0: } michael@0: michael@0: // Save the path of the given file to the specified environment variable. michael@0: static void michael@0: SaveFileToEnv(const char *name, nsIFile *file) michael@0: { michael@0: #ifdef XP_WIN michael@0: nsAutoString path; michael@0: file->GetPath(path); michael@0: SetEnvironmentVariableW(NS_ConvertASCIItoUTF16(name).get(), path.get()); michael@0: #else michael@0: nsAutoCString path; michael@0: file->GetNativePath(path); michael@0: SaveWordToEnv(name, path); michael@0: #endif michael@0: } michael@0: michael@0: // Load the path of a file saved with SaveFileToEnv michael@0: static already_AddRefed michael@0: GetFileFromEnv(const char *name) michael@0: { michael@0: nsresult rv; michael@0: nsCOMPtr file; michael@0: michael@0: #ifdef XP_WIN michael@0: WCHAR path[_MAX_PATH]; michael@0: if (!GetEnvironmentVariableW(NS_ConvertASCIItoUTF16(name).get(), michael@0: path, _MAX_PATH)) michael@0: return nullptr; michael@0: michael@0: rv = NS_NewLocalFile(nsDependentString(path), true, getter_AddRefs(file)); michael@0: if (NS_FAILED(rv)) michael@0: return nullptr; michael@0: michael@0: return file.forget(); michael@0: #else michael@0: const char *arg = PR_GetEnv(name); michael@0: if (!arg || !*arg) michael@0: return nullptr; michael@0: michael@0: rv = NS_NewNativeLocalFile(nsDependentCString(arg), true, michael@0: getter_AddRefs(file)); michael@0: if (NS_FAILED(rv)) michael@0: return nullptr; michael@0: michael@0: return file.forget(); michael@0: #endif michael@0: } michael@0: michael@0: // Save the path of the given word to the specified environment variable michael@0: // provided the environment variable does not have a value. michael@0: static void michael@0: SaveWordToEnvIfUnset(const char *name, const nsACString & word) michael@0: { michael@0: if (!EnvHasValue(name)) michael@0: SaveWordToEnv(name, word); michael@0: } michael@0: michael@0: // Save the path of the given file to the specified environment variable michael@0: // provided the environment variable does not have a value. michael@0: static void michael@0: SaveFileToEnvIfUnset(const char *name, nsIFile *file) michael@0: { michael@0: if (!EnvHasValue(name)) michael@0: SaveFileToEnv(name, file); michael@0: } michael@0: michael@0: static bool michael@0: strimatch(const char* lowerstr, const char* mixedstr) michael@0: { michael@0: while(*lowerstr) { michael@0: if (!*mixedstr) return false; // mixedstr is shorter michael@0: if (tolower(*mixedstr) != *lowerstr) return false; // no match michael@0: michael@0: ++lowerstr; michael@0: ++mixedstr; michael@0: } michael@0: michael@0: if (*mixedstr) return false; // lowerstr is shorter michael@0: michael@0: return true; michael@0: } michael@0: michael@0: /** michael@0: * Output a string to the user. This method is really only meant to be used to michael@0: * output last-ditch error messages designed for developers NOT END USERS. michael@0: * michael@0: * @param isError michael@0: * Pass true to indicate severe errors. michael@0: * @param fmt michael@0: * printf-style format string followed by arguments. michael@0: */ michael@0: static void Output(bool isError, const char *fmt, ... ) michael@0: { michael@0: va_list ap; michael@0: va_start(ap, fmt); michael@0: michael@0: #if defined(XP_WIN) && !MOZ_WINCONSOLE michael@0: char *msg = PR_vsmprintf(fmt, ap); michael@0: if (msg) michael@0: { michael@0: UINT flags = MB_OK; michael@0: if (isError) michael@0: flags |= MB_ICONERROR; michael@0: else michael@0: flags |= MB_ICONINFORMATION; michael@0: michael@0: wchar_t wide_msg[1024]; michael@0: MultiByteToWideChar(CP_ACP, michael@0: 0, michael@0: msg, michael@0: -1, michael@0: wide_msg, michael@0: sizeof(wide_msg) / sizeof(wchar_t)); michael@0: michael@0: MessageBoxW(nullptr, wide_msg, L"XULRunner", flags); michael@0: PR_smprintf_free(msg); michael@0: } michael@0: #else michael@0: vfprintf(stderr, fmt, ap); michael@0: #endif michael@0: michael@0: va_end(ap); michael@0: } michael@0: michael@0: enum RemoteResult { michael@0: REMOTE_NOT_FOUND = 0, michael@0: REMOTE_FOUND = 1, michael@0: REMOTE_ARG_BAD = 2 michael@0: }; michael@0: michael@0: enum ArgResult { michael@0: ARG_NONE = 0, michael@0: ARG_FOUND = 1, michael@0: ARG_BAD = 2 // you wanted a param, but there isn't one michael@0: }; michael@0: michael@0: static void RemoveArg(char **argv) michael@0: { michael@0: do { michael@0: *argv = *(argv + 1); michael@0: ++argv; michael@0: } while (*argv); michael@0: michael@0: --gArgc; michael@0: } michael@0: michael@0: /** michael@0: * Check for a commandline flag. If the flag takes a parameter, the michael@0: * parameter is returned in aParam. Flags may be in the form -arg or michael@0: * --arg (or /arg on win32). michael@0: * michael@0: * @param aArg the parameter to check. Must be lowercase. michael@0: * @param aCheckOSInt if true returns ARG_BAD if the osint argument is present michael@0: * when aArg is also present. michael@0: * @param aParam if non-null, the -arg will be stored in this pointer. michael@0: * This is *not* allocated, but rather a pointer to the argv data. michael@0: * @param aRemArg if true, the argument is removed from the gArgv array. michael@0: */ michael@0: static ArgResult michael@0: CheckArg(const char* aArg, bool aCheckOSInt = false, const char **aParam = nullptr, bool aRemArg = true) michael@0: { michael@0: NS_ABORT_IF_FALSE(gArgv, "gArgv must be initialized before CheckArg()"); michael@0: michael@0: char **curarg = gArgv + 1; // skip argv[0] michael@0: ArgResult ar = ARG_NONE; michael@0: michael@0: while (*curarg) { michael@0: char *arg = curarg[0]; michael@0: michael@0: if (arg[0] == '-' michael@0: #if defined(XP_WIN) michael@0: || *arg == '/' michael@0: #endif michael@0: ) { michael@0: ++arg; michael@0: if (*arg == '-') michael@0: ++arg; michael@0: michael@0: if (strimatch(aArg, arg)) { michael@0: if (aRemArg) michael@0: RemoveArg(curarg); michael@0: if (!aParam) { michael@0: ar = ARG_FOUND; michael@0: break; michael@0: } michael@0: michael@0: if (*curarg) { michael@0: if (**curarg == '-' michael@0: #if defined(XP_WIN) michael@0: || **curarg == '/' michael@0: #endif michael@0: ) michael@0: return ARG_BAD; michael@0: michael@0: *aParam = *curarg; michael@0: if (aRemArg) michael@0: RemoveArg(curarg); michael@0: ar = ARG_FOUND; michael@0: break; michael@0: } michael@0: return ARG_BAD; michael@0: } michael@0: } michael@0: michael@0: ++curarg; michael@0: } michael@0: michael@0: if (aCheckOSInt && ar == ARG_FOUND) { michael@0: ArgResult arOSInt = CheckArg("osint"); michael@0: if (arOSInt == ARG_FOUND) { michael@0: ar = ARG_BAD; michael@0: PR_fprintf(PR_STDERR, "Error: argument -osint is invalid\n"); michael@0: } michael@0: } michael@0: michael@0: return ar; michael@0: } michael@0: michael@0: #if defined(XP_WIN) michael@0: /** michael@0: * Check for a commandline flag from the windows shell and remove it from the michael@0: * argv used when restarting. Flags MUST be in the form -arg. michael@0: * michael@0: * @param aArg the parameter to check. Must be lowercase. michael@0: */ michael@0: static ArgResult michael@0: CheckArgShell(const char* aArg) michael@0: { michael@0: char **curarg = gRestartArgv + 1; // skip argv[0] michael@0: michael@0: while (*curarg) { michael@0: char *arg = curarg[0]; michael@0: michael@0: if (arg[0] == '-') { michael@0: ++arg; michael@0: michael@0: if (strimatch(aArg, arg)) { michael@0: do { michael@0: *curarg = *(curarg + 1); michael@0: ++curarg; michael@0: } while (*curarg); michael@0: michael@0: --gRestartArgc; michael@0: michael@0: return ARG_FOUND; michael@0: } michael@0: } michael@0: michael@0: ++curarg; michael@0: } michael@0: michael@0: return ARG_NONE; michael@0: } michael@0: michael@0: /** michael@0: * Enabled Native App Support to process DDE messages when the app needs to michael@0: * restart and the app has been launched by the Windows shell to open an url. michael@0: * When aWait is false this will process the DDE events manually. This prevents michael@0: * Windows from displaying an error message due to the DDE message not being michael@0: * acknowledged. michael@0: */ michael@0: static void michael@0: ProcessDDE(nsINativeAppSupport* aNative, bool aWait) michael@0: { michael@0: // When the app is launched by the windows shell the windows shell michael@0: // expects the app to be available for DDE messages and if it isn't michael@0: // windows displays an error dialog. To prevent the error the DDE server michael@0: // is enabled and pending events are processed when the app needs to michael@0: // restart after it was launched by the shell with the requestpending michael@0: // argument. The requestpending pending argument is removed to michael@0: // differentiate it from being launched when an app restart is not michael@0: // required. michael@0: ArgResult ar; michael@0: ar = CheckArgShell("requestpending"); michael@0: if (ar == ARG_FOUND) { michael@0: aNative->Enable(); // enable win32 DDE responses michael@0: if (aWait) { michael@0: nsIThread *thread = NS_GetCurrentThread(); michael@0: // This is just a guesstimate based on testing different values. michael@0: // If count is 8 or less windows will display an error dialog. michael@0: int32_t count = 20; michael@0: while(--count >= 0) { michael@0: NS_ProcessNextEvent(thread); michael@0: PR_Sleep(PR_MillisecondsToInterval(1)); michael@0: } michael@0: } michael@0: } michael@0: } michael@0: #endif michael@0: michael@0: /** michael@0: * Determines if there is support for showing the profile manager michael@0: * michael@0: * @return true in all environments except for Windows Metro michael@0: */ michael@0: static bool michael@0: CanShowProfileManager() michael@0: { michael@0: #if defined(XP_WIN) michael@0: return XRE_GetWindowsEnvironment() == WindowsEnvironmentType_Desktop; michael@0: #else michael@0: return true; michael@0: #endif michael@0: } michael@0: michael@0: michael@0: bool gSafeMode = false; michael@0: michael@0: /** michael@0: * The nsXULAppInfo object implements nsIFactory so that it can be its own michael@0: * singleton. michael@0: */ michael@0: class nsXULAppInfo : public nsIXULAppInfo, michael@0: #ifdef XP_WIN michael@0: public nsIWinAppHelper, michael@0: #endif michael@0: #ifdef MOZ_CRASHREPORTER michael@0: public nsICrashReporter, michael@0: #endif michael@0: public nsIXULRuntime michael@0: michael@0: { michael@0: public: michael@0: MOZ_CONSTEXPR nsXULAppInfo() {} michael@0: NS_DECL_ISUPPORTS_INHERITED michael@0: NS_DECL_NSIXULAPPINFO michael@0: NS_DECL_NSIXULRUNTIME michael@0: #ifdef MOZ_CRASHREPORTER michael@0: NS_DECL_NSICRASHREPORTER michael@0: #endif michael@0: #ifdef XP_WIN michael@0: NS_DECL_NSIWINAPPHELPER michael@0: #endif michael@0: }; michael@0: michael@0: NS_INTERFACE_MAP_BEGIN(nsXULAppInfo) michael@0: NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIXULRuntime) michael@0: NS_INTERFACE_MAP_ENTRY(nsIXULRuntime) michael@0: #ifdef XP_WIN michael@0: NS_INTERFACE_MAP_ENTRY(nsIWinAppHelper) michael@0: #endif michael@0: #ifdef MOZ_CRASHREPORTER michael@0: NS_INTERFACE_MAP_ENTRY(nsICrashReporter) michael@0: #endif michael@0: NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIXULAppInfo, gAppData || michael@0: XRE_GetProcessType() == GeckoProcessType_Content) michael@0: NS_INTERFACE_MAP_END michael@0: michael@0: NS_IMETHODIMP_(MozExternalRefCountType) michael@0: nsXULAppInfo::AddRef() michael@0: { michael@0: return 1; michael@0: } michael@0: michael@0: NS_IMETHODIMP_(MozExternalRefCountType) michael@0: nsXULAppInfo::Release() michael@0: { michael@0: return 1; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsXULAppInfo::GetVendor(nsACString& aResult) michael@0: { michael@0: if (XRE_GetProcessType() == GeckoProcessType_Content) { michael@0: return NS_ERROR_NOT_AVAILABLE; michael@0: } michael@0: aResult.Assign(gAppData->vendor); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsXULAppInfo::GetName(nsACString& aResult) michael@0: { michael@0: if (XRE_GetProcessType() == GeckoProcessType_Content) { michael@0: ContentChild* cc = ContentChild::GetSingleton(); michael@0: aResult = cc->GetAppInfo().name; michael@0: return NS_OK; michael@0: } michael@0: aResult.Assign(gAppData->name); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsXULAppInfo::GetID(nsACString& aResult) michael@0: { michael@0: if (XRE_GetProcessType() == GeckoProcessType_Content) { michael@0: return NS_ERROR_NOT_AVAILABLE; michael@0: } michael@0: aResult.Assign(gAppData->ID); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsXULAppInfo::GetVersion(nsACString& aResult) michael@0: { michael@0: if (XRE_GetProcessType() == GeckoProcessType_Content) { michael@0: ContentChild* cc = ContentChild::GetSingleton(); michael@0: aResult = cc->GetAppInfo().version; michael@0: return NS_OK; michael@0: } michael@0: aResult.Assign(gAppData->version); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsXULAppInfo::GetPlatformVersion(nsACString& aResult) michael@0: { michael@0: aResult.Assign(gToolkitVersion); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsXULAppInfo::GetAppBuildID(nsACString& aResult) michael@0: { michael@0: if (XRE_GetProcessType() == GeckoProcessType_Content) { michael@0: ContentChild* cc = ContentChild::GetSingleton(); michael@0: aResult = cc->GetAppInfo().buildID; michael@0: return NS_OK; michael@0: } michael@0: aResult.Assign(gAppData->buildID); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsXULAppInfo::GetPlatformBuildID(nsACString& aResult) michael@0: { michael@0: aResult.Assign(gToolkitBuildID); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsXULAppInfo::GetUAName(nsACString& aResult) michael@0: { michael@0: if (XRE_GetProcessType() == GeckoProcessType_Content) { michael@0: ContentChild* cc = ContentChild::GetSingleton(); michael@0: aResult = cc->GetAppInfo().UAName; michael@0: return NS_OK; michael@0: } michael@0: aResult.Assign(gAppData->UAName); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsXULAppInfo::GetLogConsoleErrors(bool *aResult) michael@0: { michael@0: *aResult = gLogConsoleErrors; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsXULAppInfo::SetLogConsoleErrors(bool aValue) michael@0: { michael@0: gLogConsoleErrors = aValue; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsXULAppInfo::GetInSafeMode(bool *aResult) michael@0: { michael@0: *aResult = gSafeMode; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsXULAppInfo::GetOS(nsACString& aResult) michael@0: { michael@0: aResult.AssignLiteral(OS_TARGET); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsXULAppInfo::GetXPCOMABI(nsACString& aResult) michael@0: { michael@0: #ifdef TARGET_XPCOM_ABI michael@0: aResult.AssignLiteral(TARGET_XPCOM_ABI); michael@0: return NS_OK; michael@0: #else michael@0: return NS_ERROR_NOT_AVAILABLE; michael@0: #endif michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsXULAppInfo::GetWidgetToolkit(nsACString& aResult) michael@0: { michael@0: aResult.AssignLiteral(MOZ_WIDGET_TOOLKIT); michael@0: return NS_OK; michael@0: } michael@0: michael@0: // Ensure that the GeckoProcessType enum, defined in xpcom/build/nsXULAppAPI.h, michael@0: // is synchronized with the const unsigned longs defined in michael@0: // xpcom/system/nsIXULRuntime.idl. michael@0: #define SYNC_ENUMS(a,b) \ michael@0: static_assert(nsIXULRuntime::PROCESS_TYPE_ ## a == \ michael@0: static_cast(GeckoProcessType_ ## b), \ michael@0: "GeckoProcessType in nsXULAppAPI.h not synchronized with nsIXULRuntime.idl"); michael@0: michael@0: SYNC_ENUMS(DEFAULT, Default) michael@0: SYNC_ENUMS(PLUGIN, Plugin) michael@0: SYNC_ENUMS(CONTENT, Content) michael@0: SYNC_ENUMS(IPDLUNITTEST, IPDLUnitTest) michael@0: michael@0: // .. and ensure that that is all of them: michael@0: static_assert(GeckoProcessType_IPDLUnitTest + 1 == GeckoProcessType_End, michael@0: "Did not find the final GeckoProcessType"); michael@0: michael@0: NS_IMETHODIMP michael@0: nsXULAppInfo::GetProcessType(uint32_t* aResult) michael@0: { michael@0: NS_ENSURE_ARG_POINTER(aResult); michael@0: *aResult = XRE_GetProcessType(); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsXULAppInfo::GetProcessID(uint32_t* aResult) michael@0: { michael@0: #ifdef XP_WIN michael@0: *aResult = GetCurrentProcessId(); michael@0: #else michael@0: *aResult = getpid(); michael@0: #endif michael@0: return NS_OK; michael@0: } michael@0: michael@0: static bool gBrowserTabsRemote = false; michael@0: static bool gBrowserTabsRemoteInitialized = false; michael@0: michael@0: NS_IMETHODIMP michael@0: nsXULAppInfo::GetBrowserTabsRemote(bool* aResult) michael@0: { michael@0: *aResult = BrowserTabsRemote(); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsXULAppInfo::EnsureContentProcess() michael@0: { michael@0: if (XRE_GetProcessType() != GeckoProcessType_Default) michael@0: return NS_ERROR_NOT_AVAILABLE; michael@0: michael@0: nsRefPtr unused = ContentParent::GetNewOrUsed(); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsXULAppInfo::InvalidateCachesOnRestart() michael@0: { michael@0: nsCOMPtr file; michael@0: nsresult rv = NS_GetSpecialDirectory(NS_APP_PROFILE_DIR_STARTUP, michael@0: getter_AddRefs(file)); michael@0: if (NS_FAILED(rv)) michael@0: return rv; michael@0: if (!file) michael@0: return NS_ERROR_NOT_AVAILABLE; michael@0: michael@0: file->AppendNative(FILE_COMPATIBILITY_INFO); michael@0: michael@0: nsINIParser parser; michael@0: rv = parser.Init(file); michael@0: if (NS_FAILED(rv)) { michael@0: // This fails if compatibility.ini is not there, so we'll michael@0: // flush the caches on the next restart anyways. michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsAutoCString buf; michael@0: rv = parser.GetString("Compatibility", "InvalidateCaches", buf); michael@0: michael@0: if (NS_FAILED(rv)) { michael@0: PRFileDesc *fd = nullptr; michael@0: file->OpenNSPRFileDesc(PR_RDWR | PR_APPEND, 0600, &fd); michael@0: if (!fd) { michael@0: NS_ERROR("could not create output stream"); michael@0: return NS_ERROR_NOT_AVAILABLE; michael@0: } michael@0: static const char kInvalidationHeader[] = NS_LINEBREAK "InvalidateCaches=1" NS_LINEBREAK; michael@0: PR_Write(fd, kInvalidationHeader, sizeof(kInvalidationHeader) - 1); michael@0: PR_Close(fd); michael@0: } michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsXULAppInfo::GetReplacedLockTime(PRTime *aReplacedLockTime) michael@0: { michael@0: if (!gProfileLock) michael@0: return NS_ERROR_NOT_AVAILABLE; michael@0: gProfileLock->GetReplacedLockTime(aReplacedLockTime); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsXULAppInfo::GetLastRunCrashID(nsAString &aLastRunCrashID) michael@0: { michael@0: #ifdef MOZ_CRASHREPORTER michael@0: CrashReporter::GetLastRunCrashID(aLastRunCrashID); michael@0: return NS_OK; michael@0: #else michael@0: return NS_ERROR_NOT_IMPLEMENTED; michael@0: #endif michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsXULAppInfo::GetIsReleaseBuild(bool* aResult) michael@0: { michael@0: #ifdef RELEASE_BUILD michael@0: *aResult = true; michael@0: #else michael@0: *aResult = false; michael@0: #endif michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsXULAppInfo::GetIsOfficialBranding(bool* aResult) michael@0: { michael@0: #ifdef MOZ_OFFICIAL_BRANDING michael@0: *aResult = true; michael@0: #else michael@0: *aResult = false; michael@0: #endif michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsXULAppInfo::GetDefaultUpdateChannel(nsACString& aResult) michael@0: { michael@0: aResult.AssignLiteral(NS_STRINGIFY(MOZ_UPDATE_CHANNEL)); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsXULAppInfo::GetDistributionID(nsACString& aResult) michael@0: { michael@0: aResult.AssignLiteral(MOZ_DISTRIBUTION_ID); michael@0: return NS_OK; michael@0: } michael@0: michael@0: #ifdef XP_WIN michael@0: // Matches the enum in WinNT.h for the Vista SDK but renamed so that we can michael@0: // safely build with the Vista SDK and without it. michael@0: typedef enum michael@0: { michael@0: VistaTokenElevationTypeDefault = 1, michael@0: VistaTokenElevationTypeFull, michael@0: VistaTokenElevationTypeLimited michael@0: } VISTA_TOKEN_ELEVATION_TYPE; michael@0: michael@0: // avoid collision with TokeElevationType enum in WinNT.h michael@0: // of the Vista SDK michael@0: #define VistaTokenElevationType static_cast< TOKEN_INFORMATION_CLASS >( 18 ) michael@0: michael@0: NS_IMETHODIMP michael@0: nsXULAppInfo::GetUserCanElevate(bool *aUserCanElevate) michael@0: { michael@0: HANDLE hToken; michael@0: michael@0: VISTA_TOKEN_ELEVATION_TYPE elevationType; michael@0: DWORD dwSize; michael@0: michael@0: if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hToken) || michael@0: !GetTokenInformation(hToken, VistaTokenElevationType, &elevationType, michael@0: sizeof(elevationType), &dwSize)) { michael@0: *aUserCanElevate = false; michael@0: } michael@0: else { michael@0: // The possible values returned for elevationType and their meanings are: michael@0: // TokenElevationTypeDefault: The token does not have a linked token michael@0: // (e.g. UAC disabled or a standard user, so they can't be elevated) michael@0: // TokenElevationTypeFull: The token is linked to an elevated token michael@0: // (e.g. UAC is enabled and the user is already elevated so they can't michael@0: // be elevated again) michael@0: // TokenElevationTypeLimited: The token is linked to a limited token michael@0: // (e.g. UAC is enabled and the user is not elevated, so they can be michael@0: // elevated) michael@0: *aUserCanElevate = (elevationType == VistaTokenElevationTypeLimited); michael@0: } michael@0: michael@0: if (hToken) michael@0: CloseHandle(hToken); michael@0: michael@0: return NS_OK; michael@0: } michael@0: #endif michael@0: michael@0: #ifdef MOZ_CRASHREPORTER michael@0: NS_IMETHODIMP michael@0: nsXULAppInfo::GetEnabled(bool *aEnabled) michael@0: { michael@0: *aEnabled = CrashReporter::GetEnabled(); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsXULAppInfo::SetEnabled(bool aEnabled) michael@0: { michael@0: if (aEnabled) { michael@0: if (CrashReporter::GetEnabled()) michael@0: // no point in erroring for double-enabling michael@0: return NS_OK; michael@0: michael@0: nsCOMPtr xreDirectory; michael@0: if (gAppData) { michael@0: xreDirectory = gAppData->xreDirectory; michael@0: } michael@0: else { michael@0: // We didn't get started through XRE_Main, probably michael@0: nsCOMPtr greDir; michael@0: NS_GetSpecialDirectory(NS_GRE_DIR, getter_AddRefs(greDir)); michael@0: if (!greDir) michael@0: return NS_ERROR_FAILURE; michael@0: michael@0: xreDirectory = do_QueryInterface(greDir); michael@0: if (!xreDirectory) michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: return CrashReporter::SetExceptionHandler(xreDirectory, true); michael@0: } michael@0: else { michael@0: if (!CrashReporter::GetEnabled()) michael@0: // no point in erroring for double-disabling michael@0: return NS_OK; michael@0: michael@0: return CrashReporter::UnsetExceptionHandler(); michael@0: } michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsXULAppInfo::GetServerURL(nsIURL** aServerURL) michael@0: { michael@0: if (!CrashReporter::GetEnabled()) michael@0: return NS_ERROR_NOT_INITIALIZED; michael@0: michael@0: nsAutoCString data; michael@0: if (!CrashReporter::GetServerURL(data)) { michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: nsCOMPtr uri; michael@0: NS_NewURI(getter_AddRefs(uri), data); michael@0: if (!uri) michael@0: return NS_ERROR_FAILURE; michael@0: michael@0: nsCOMPtr url; michael@0: url = do_QueryInterface(uri); michael@0: NS_ADDREF(*aServerURL = url); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsXULAppInfo::SetServerURL(nsIURL* aServerURL) michael@0: { michael@0: bool schemeOk; michael@0: // only allow https or http URLs michael@0: nsresult rv = aServerURL->SchemeIs("https", &schemeOk); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: if (!schemeOk) { michael@0: rv = aServerURL->SchemeIs("http", &schemeOk); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: if (!schemeOk) michael@0: return NS_ERROR_INVALID_ARG; michael@0: } michael@0: nsAutoCString spec; michael@0: rv = aServerURL->GetSpec(spec); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: return CrashReporter::SetServerURL(spec); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsXULAppInfo::GetMinidumpPath(nsIFile** aMinidumpPath) michael@0: { michael@0: if (!CrashReporter::GetEnabled()) michael@0: return NS_ERROR_NOT_INITIALIZED; michael@0: michael@0: nsAutoString path; michael@0: if (!CrashReporter::GetMinidumpPath(path)) michael@0: return NS_ERROR_FAILURE; michael@0: michael@0: nsresult rv = NS_NewLocalFile(path, false, aMinidumpPath); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsXULAppInfo::SetMinidumpPath(nsIFile* aMinidumpPath) michael@0: { michael@0: nsAutoString path; michael@0: nsresult rv = aMinidumpPath->GetPath(path); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: return CrashReporter::SetMinidumpPath(path); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsXULAppInfo::AnnotateCrashReport(const nsACString& key, michael@0: const nsACString& data) michael@0: { michael@0: return CrashReporter::AnnotateCrashReport(key, data); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsXULAppInfo::AppendAppNotesToCrashReport(const nsACString& data) michael@0: { michael@0: return CrashReporter::AppendAppNotesToCrashReport(data); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsXULAppInfo::RegisterAppMemory(uint64_t pointer, michael@0: uint64_t len) michael@0: { michael@0: return CrashReporter::RegisterAppMemory((void *)pointer, len); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsXULAppInfo::WriteMinidumpForException(void* aExceptionInfo) michael@0: { michael@0: #ifdef XP_WIN32 michael@0: return CrashReporter::WriteMinidumpForException(static_cast(aExceptionInfo)); michael@0: #else michael@0: return NS_ERROR_NOT_IMPLEMENTED; michael@0: #endif michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsXULAppInfo::AppendObjCExceptionInfoToAppNotes(void* aException) michael@0: { michael@0: #ifdef XP_MACOSX michael@0: return CrashReporter::AppendObjCExceptionInfoToAppNotes(aException); michael@0: #else michael@0: return NS_ERROR_NOT_IMPLEMENTED; michael@0: #endif michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsXULAppInfo::GetSubmitReports(bool* aEnabled) michael@0: { michael@0: return CrashReporter::GetSubmitReports(aEnabled); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsXULAppInfo::SetSubmitReports(bool aEnabled) michael@0: { michael@0: return CrashReporter::SetSubmitReports(aEnabled); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsXULAppInfo::UpdateCrashEventsDir() michael@0: { michael@0: CrashReporter::UpdateCrashEventsDir(); michael@0: return NS_OK; michael@0: } michael@0: michael@0: #endif michael@0: michael@0: static const nsXULAppInfo kAppInfo; michael@0: static nsresult AppInfoConstructor(nsISupports* aOuter, michael@0: REFNSIID aIID, void **aResult) michael@0: { michael@0: NS_ENSURE_NO_AGGREGATION(aOuter); michael@0: michael@0: return const_cast(&kAppInfo)-> michael@0: QueryInterface(aIID, aResult); michael@0: } michael@0: michael@0: bool gLogConsoleErrors = false; michael@0: michael@0: #define NS_ENSURE_TRUE_LOG(x, ret) \ michael@0: PR_BEGIN_MACRO \ michael@0: if (MOZ_UNLIKELY(!(x))) { \ michael@0: NS_WARNING("NS_ENSURE_TRUE(" #x ") failed"); \ michael@0: gLogConsoleErrors = true; \ michael@0: return ret; \ michael@0: } \ michael@0: PR_END_MACRO michael@0: michael@0: #define NS_ENSURE_SUCCESS_LOG(res, ret) \ michael@0: NS_ENSURE_TRUE_LOG(NS_SUCCEEDED(res), ret) michael@0: michael@0: /** michael@0: * Because we're starting/stopping XPCOM several times in different scenarios, michael@0: * this class is a stack-based critter that makes sure that XPCOM is shut down michael@0: * during early returns. michael@0: */ michael@0: michael@0: class ScopedXPCOMStartup michael@0: { michael@0: public: michael@0: ScopedXPCOMStartup() : michael@0: mServiceManager(nullptr) { } michael@0: ~ScopedXPCOMStartup(); michael@0: michael@0: nsresult Initialize(); michael@0: nsresult SetWindowCreator(nsINativeAppSupport* native); michael@0: michael@0: static nsresult CreateAppSupport(nsISupports* aOuter, REFNSIID aIID, void** aResult); michael@0: michael@0: private: michael@0: nsIServiceManager* mServiceManager; michael@0: static nsINativeAppSupport* gNativeAppSupport; michael@0: }; michael@0: michael@0: ScopedXPCOMStartup::~ScopedXPCOMStartup() michael@0: { michael@0: NS_IF_RELEASE(gNativeAppSupport); michael@0: michael@0: if (mServiceManager) { michael@0: #ifdef XP_MACOSX michael@0: // On OS X, we need a pool to catch cocoa objects that are autoreleased michael@0: // during teardown. michael@0: mozilla::MacAutoreleasePool pool; michael@0: #endif michael@0: michael@0: nsCOMPtr appStartup (do_GetService(NS_APPSTARTUP_CONTRACTID)); michael@0: if (appStartup) michael@0: appStartup->DestroyHiddenWindow(); michael@0: michael@0: gDirServiceProvider->DoShutdown(); michael@0: PROFILER_MARKER("Shutdown early"); michael@0: michael@0: WriteConsoleLog(); michael@0: michael@0: NS_ShutdownXPCOM(mServiceManager); michael@0: mServiceManager = nullptr; michael@0: } michael@0: } michael@0: michael@0: // {95d89e3e-a169-41a3-8e56-719978e15b12} michael@0: #define APPINFO_CID \ michael@0: { 0x95d89e3e, 0xa169, 0x41a3, { 0x8e, 0x56, 0x71, 0x99, 0x78, 0xe1, 0x5b, 0x12 } } michael@0: michael@0: // {0C4A446C-EE82-41f2-8D04-D366D2C7A7D4} michael@0: static const nsCID kNativeAppSupportCID = michael@0: { 0xc4a446c, 0xee82, 0x41f2, { 0x8d, 0x4, 0xd3, 0x66, 0xd2, 0xc7, 0xa7, 0xd4 } }; michael@0: michael@0: // {5F5E59CE-27BC-47eb-9D1F-B09CA9049836} michael@0: static const nsCID kProfileServiceCID = michael@0: { 0x5f5e59ce, 0x27bc, 0x47eb, { 0x9d, 0x1f, 0xb0, 0x9c, 0xa9, 0x4, 0x98, 0x36 } }; michael@0: michael@0: static already_AddRefed michael@0: ProfileServiceFactoryConstructor(const mozilla::Module& module, const mozilla::Module::CIDEntry& entry) michael@0: { michael@0: nsCOMPtr factory; michael@0: NS_NewToolkitProfileFactory(getter_AddRefs(factory)); michael@0: return factory.forget(); michael@0: } michael@0: michael@0: NS_DEFINE_NAMED_CID(APPINFO_CID); michael@0: michael@0: static const mozilla::Module::CIDEntry kXRECIDs[] = { michael@0: { &kAPPINFO_CID, false, nullptr, AppInfoConstructor }, michael@0: { &kProfileServiceCID, false, ProfileServiceFactoryConstructor, nullptr }, michael@0: { &kNativeAppSupportCID, false, nullptr, ScopedXPCOMStartup::CreateAppSupport }, michael@0: { nullptr } michael@0: }; michael@0: michael@0: static const mozilla::Module::ContractIDEntry kXREContracts[] = { michael@0: { XULAPPINFO_SERVICE_CONTRACTID, &kAPPINFO_CID }, michael@0: { XULRUNTIME_SERVICE_CONTRACTID, &kAPPINFO_CID }, michael@0: #ifdef MOZ_CRASHREPORTER michael@0: { NS_CRASHREPORTER_CONTRACTID, &kAPPINFO_CID }, michael@0: #endif michael@0: { NS_PROFILESERVICE_CONTRACTID, &kProfileServiceCID }, michael@0: { NS_NATIVEAPPSUPPORT_CONTRACTID, &kNativeAppSupportCID }, michael@0: { nullptr } michael@0: }; michael@0: michael@0: static const mozilla::Module kXREModule = { michael@0: mozilla::Module::kVersion, michael@0: kXRECIDs, michael@0: kXREContracts michael@0: }; michael@0: michael@0: NSMODULE_DEFN(Apprunner) = &kXREModule; michael@0: michael@0: nsresult michael@0: ScopedXPCOMStartup::Initialize() michael@0: { michael@0: NS_ASSERTION(gDirServiceProvider, "Should not get here!"); michael@0: michael@0: nsresult rv; michael@0: michael@0: rv = NS_InitXPCOM2(&mServiceManager, gDirServiceProvider->GetAppDir(), michael@0: gDirServiceProvider); michael@0: if (NS_FAILED(rv)) { michael@0: NS_ERROR("Couldn't start xpcom!"); michael@0: mServiceManager = nullptr; michael@0: } michael@0: else { michael@0: nsCOMPtr reg = michael@0: do_QueryInterface(mServiceManager); michael@0: NS_ASSERTION(reg, "Service Manager doesn't QI to Registrar."); michael@0: } michael@0: michael@0: return rv; michael@0: } michael@0: michael@0: /** michael@0: * This is a little factory class that serves as a singleton-service-factory michael@0: * for the nativeappsupport object. michael@0: */ michael@0: class nsSingletonFactory MOZ_FINAL : public nsIFactory michael@0: { michael@0: public: michael@0: NS_DECL_ISUPPORTS michael@0: NS_DECL_NSIFACTORY michael@0: michael@0: nsSingletonFactory(nsISupports* aSingleton); michael@0: ~nsSingletonFactory() { } michael@0: michael@0: private: michael@0: nsCOMPtr mSingleton; michael@0: }; michael@0: michael@0: nsSingletonFactory::nsSingletonFactory(nsISupports* aSingleton) michael@0: : mSingleton(aSingleton) michael@0: { michael@0: NS_ASSERTION(mSingleton, "Singleton was null!"); michael@0: } michael@0: michael@0: NS_IMPL_ISUPPORTS(nsSingletonFactory, nsIFactory) michael@0: michael@0: NS_IMETHODIMP michael@0: nsSingletonFactory::CreateInstance(nsISupports* aOuter, michael@0: const nsIID& aIID, michael@0: void* *aResult) michael@0: { michael@0: NS_ENSURE_NO_AGGREGATION(aOuter); michael@0: michael@0: return mSingleton->QueryInterface(aIID, aResult); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsSingletonFactory::LockFactory(bool) michael@0: { michael@0: return NS_OK; michael@0: } michael@0: michael@0: /** michael@0: * Set our windowcreator on the WindowWatcher service. michael@0: */ michael@0: nsresult michael@0: ScopedXPCOMStartup::SetWindowCreator(nsINativeAppSupport* native) michael@0: { michael@0: nsresult rv; michael@0: michael@0: NS_IF_ADDREF(gNativeAppSupport = native); michael@0: michael@0: // Inform the chrome registry about OS accessibility michael@0: nsCOMPtr cr = michael@0: mozilla::services::GetToolkitChromeRegistryService(); michael@0: michael@0: if (cr) michael@0: cr->CheckForOSAccessibility(); michael@0: michael@0: nsCOMPtr creator (do_GetService(NS_APPSTARTUP_CONTRACTID)); michael@0: if (!creator) return NS_ERROR_UNEXPECTED; michael@0: michael@0: nsCOMPtr wwatch michael@0: (do_GetService(NS_WINDOWWATCHER_CONTRACTID, &rv)); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: return wwatch->SetWindowCreator(creator); michael@0: } michael@0: michael@0: /* static */ nsresult michael@0: ScopedXPCOMStartup::CreateAppSupport(nsISupports* aOuter, REFNSIID aIID, void** aResult) michael@0: { michael@0: if (aOuter) michael@0: return NS_ERROR_NO_AGGREGATION; michael@0: michael@0: if (!gNativeAppSupport) michael@0: return NS_ERROR_NOT_INITIALIZED; michael@0: michael@0: return gNativeAppSupport->QueryInterface(aIID, aResult); michael@0: } michael@0: michael@0: nsINativeAppSupport* ScopedXPCOMStartup::gNativeAppSupport; michael@0: michael@0: /** michael@0: * A helper class which calls NS_LogInit/NS_LogTerm in its scope. michael@0: */ michael@0: class ScopedLogging michael@0: { michael@0: public: michael@0: ScopedLogging() { NS_LogInit(); } michael@0: ~ScopedLogging() { NS_LogTerm(); } michael@0: }; michael@0: michael@0: static void DumpArbitraryHelp() michael@0: { michael@0: nsresult rv; michael@0: michael@0: ScopedLogging log; michael@0: michael@0: { michael@0: ScopedXPCOMStartup xpcom; michael@0: xpcom.Initialize(); michael@0: michael@0: nsCOMPtr cmdline michael@0: (do_CreateInstance("@mozilla.org/toolkit/command-line;1")); michael@0: if (!cmdline) michael@0: return; michael@0: michael@0: nsCString text; michael@0: rv = cmdline->GetHelpText(text); michael@0: if (NS_SUCCEEDED(rv)) michael@0: printf("%s", text.get()); michael@0: } michael@0: } michael@0: michael@0: // English text needs to go into a dtd file. michael@0: // But when this is called we have no components etc. These strings must either be michael@0: // here, or in a native resource file. michael@0: static void michael@0: DumpHelp() michael@0: { michael@0: printf("Usage: %s [ options ... ] [URL]\n" michael@0: " where options include:\n\n", gArgv[0]); michael@0: michael@0: #ifdef MOZ_X11 michael@0: printf("X11 options\n" michael@0: " --display=DISPLAY X display to use\n" michael@0: " --sync Make X calls synchronous\n"); michael@0: #endif michael@0: #ifdef XP_UNIX michael@0: printf(" --g-fatal-warnings Make all warnings fatal\n" michael@0: "\n%s options\n", gAppData->name); michael@0: #endif michael@0: michael@0: printf(" -h or -help Print this message.\n" michael@0: " -v or -version Print %s version.\n" michael@0: " -P Start with .\n" michael@0: " -migration Start with migration wizard.\n" michael@0: " -ProfileManager Start with ProfileManager.\n" michael@0: " -no-remote (default) Do not accept or send remote commands; implies -new-instance.\n" michael@0: " -allow-remote Accept and send remote commands.\n" michael@0: " -new-instance Open new instance, not a new window in running instance.\n" michael@0: " -UILocale Start with resources as UI Locale.\n" michael@0: " -safe-mode Disables extensions and themes for this session.\n", gAppData->name); michael@0: michael@0: #if defined(XP_WIN) michael@0: printf(" -console Start %s with a debugging console.\n", gAppData->name); michael@0: #endif michael@0: michael@0: // this works, but only after the components have registered. so if you drop in a new command line handler, -help michael@0: // won't not until the second run. michael@0: // out of the bug, because we ship a component.reg file, it works correctly. michael@0: DumpArbitraryHelp(); michael@0: } michael@0: michael@0: #if defined(DEBUG) && defined(XP_WIN) michael@0: #ifdef DEBUG_warren michael@0: #define _CRTDBG_MAP_ALLOC michael@0: #endif michael@0: // Set a CRT ReportHook function to capture and format MSCRT michael@0: // warnings, errors and assertions. michael@0: // See http://msdn.microsoft.com/en-US/library/74kabxyx(v=VS.80).aspx michael@0: #include michael@0: #include michael@0: #include "mozilla/mozalloc_abort.h" michael@0: static int MSCRTReportHook( int aReportType, char *aMessage, int *oReturnValue) michael@0: { michael@0: *oReturnValue = 0; // continue execution michael@0: michael@0: // Do not use fprintf or other functions which may allocate michael@0: // memory from the heap which may be corrupted. Instead, michael@0: // use fputs to output the leading portion of the message michael@0: // and use mozalloc_abort to emit the remainder of the michael@0: // message. michael@0: michael@0: switch(aReportType) { michael@0: case 0: michael@0: fputs("\nWARNING: CRT WARNING", stderr); michael@0: fputs(aMessage, stderr); michael@0: fputs("\n", stderr); michael@0: break; michael@0: case 1: michael@0: fputs("\n###!!! ABORT: CRT ERROR ", stderr); michael@0: mozalloc_abort(aMessage); michael@0: break; michael@0: case 2: michael@0: fputs("\n###!!! ABORT: CRT ASSERT ", stderr); michael@0: mozalloc_abort(aMessage); michael@0: break; michael@0: } michael@0: michael@0: // do not invoke the debugger michael@0: return 1; michael@0: } michael@0: michael@0: #endif michael@0: michael@0: static inline void michael@0: DumpVersion() michael@0: { michael@0: if (gAppData->vendor) michael@0: printf("%s ", gAppData->vendor); michael@0: printf("%s %s", gAppData->name, gAppData->version); michael@0: if (gAppData->copyright) michael@0: printf(", %s", gAppData->copyright); michael@0: printf("\n"); michael@0: } michael@0: michael@0: #ifdef MOZ_ENABLE_XREMOTE michael@0: // use int here instead of a PR type since it will be returned michael@0: // from main - just to keep types consistent michael@0: static int michael@0: HandleRemoteArgument(const char* remote, const char* aDesktopStartupID) michael@0: { michael@0: nsresult rv; michael@0: ArgResult ar; michael@0: michael@0: const char *profile = 0; michael@0: nsAutoCString program(gAppData->name); michael@0: ToLowerCase(program); michael@0: const char *username = getenv("LOGNAME"); michael@0: michael@0: ar = CheckArg("p", false, &profile); michael@0: if (ar == ARG_BAD) { michael@0: PR_fprintf(PR_STDERR, "Error: argument -p requires a profile name\n"); michael@0: return 1; michael@0: } michael@0: michael@0: const char *temp = nullptr; michael@0: ar = CheckArg("a", false, &temp); michael@0: if (ar == ARG_BAD) { michael@0: PR_fprintf(PR_STDERR, "Error: argument -a requires an application name\n"); michael@0: return 1; michael@0: } else if (ar == ARG_FOUND) { michael@0: program.Assign(temp); michael@0: } michael@0: michael@0: ar = CheckArg("u", false, &username); michael@0: if (ar == ARG_BAD) { michael@0: PR_fprintf(PR_STDERR, "Error: argument -u requires a username\n"); michael@0: return 1; michael@0: } michael@0: michael@0: XRemoteClient client; michael@0: rv = client.Init(); michael@0: if (NS_FAILED(rv)) { michael@0: PR_fprintf(PR_STDERR, "Error: Failed to connect to X server.\n"); michael@0: return 1; michael@0: } michael@0: michael@0: nsXPIDLCString response; michael@0: bool success = false; michael@0: rv = client.SendCommand(program.get(), username, profile, remote, michael@0: aDesktopStartupID, getter_Copies(response), &success); michael@0: // did the command fail? michael@0: if (NS_FAILED(rv)) { michael@0: PR_fprintf(PR_STDERR, "Error: Failed to send command: %s\n", michael@0: response ? response.get() : "No response included"); michael@0: return 1; michael@0: } michael@0: michael@0: if (!success) { michael@0: PR_fprintf(PR_STDERR, "Error: No running window found\n"); michael@0: return 2; michael@0: } michael@0: michael@0: return 0; michael@0: } michael@0: michael@0: static RemoteResult michael@0: RemoteCommandLine(const char* aDesktopStartupID) michael@0: { michael@0: nsresult rv; michael@0: ArgResult ar; michael@0: michael@0: nsAutoCString program(gAppData->name); michael@0: ToLowerCase(program); michael@0: const char *username = getenv("LOGNAME"); michael@0: michael@0: const char *temp = nullptr; michael@0: ar = CheckArg("a", true, &temp); michael@0: if (ar == ARG_BAD) { michael@0: PR_fprintf(PR_STDERR, "Error: argument -a requires an application name\n"); michael@0: return REMOTE_ARG_BAD; michael@0: } else if (ar == ARG_FOUND) { michael@0: program.Assign(temp); michael@0: } michael@0: michael@0: ar = CheckArg("u", true, &username); michael@0: if (ar == ARG_BAD) { michael@0: PR_fprintf(PR_STDERR, "Error: argument -u requires a username\n"); michael@0: return REMOTE_ARG_BAD; michael@0: } michael@0: michael@0: XRemoteClient client; michael@0: rv = client.Init(); michael@0: if (NS_FAILED(rv)) michael@0: return REMOTE_NOT_FOUND; michael@0: michael@0: nsXPIDLCString response; michael@0: bool success = false; michael@0: rv = client.SendCommandLine(program.get(), username, nullptr, michael@0: gArgc, gArgv, aDesktopStartupID, michael@0: getter_Copies(response), &success); michael@0: // did the command fail? michael@0: if (NS_FAILED(rv) || !success) michael@0: return REMOTE_NOT_FOUND; michael@0: michael@0: return REMOTE_FOUND; michael@0: } michael@0: #endif // MOZ_ENABLE_XREMOTE michael@0: michael@0: void michael@0: XRE_InitOmnijar(nsIFile* greOmni, nsIFile* appOmni) michael@0: { michael@0: mozilla::Omnijar::Init(greOmni, appOmni); michael@0: } michael@0: michael@0: nsresult michael@0: XRE_GetBinaryPath(const char* argv0, nsIFile* *aResult) michael@0: { michael@0: return mozilla::BinaryPath::GetFile(argv0, aResult); michael@0: } michael@0: michael@0: #ifdef XP_WIN michael@0: #include "nsWindowsRestart.cpp" michael@0: #include michael@0: michael@0: typedef BOOL (WINAPI* SetProcessDEPPolicyFunc)(DWORD dwFlags); michael@0: #endif michael@0: michael@0: // If aBlankCommandLine is true, then the application will be launched with a michael@0: // blank command line instead of being launched with the same command line that michael@0: // it was initially started with. michael@0: static nsresult LaunchChild(nsINativeAppSupport* aNative, michael@0: bool aBlankCommandLine = false) michael@0: { michael@0: aNative->Quit(); // release DDE mutex, if we're holding it michael@0: michael@0: // Restart this process by exec'ing it into the current process michael@0: // if supported by the platform. Otherwise, use NSPR. michael@0: michael@0: #ifdef MOZ_JPROF michael@0: // make sure JPROF doesn't think we're E10s michael@0: unsetenv("JPROF_SLAVE"); michael@0: #endif michael@0: michael@0: if (aBlankCommandLine) { michael@0: #if defined(MOZ_WIDGET_QT) michael@0: // Remove only arguments not given to Qt michael@0: gRestartArgc = gQtOnlyArgc; michael@0: gRestartArgv = gQtOnlyArgv; michael@0: #else michael@0: gRestartArgc = 1; michael@0: gRestartArgv[gRestartArgc] = nullptr; michael@0: #endif michael@0: } michael@0: michael@0: SaveToEnv("MOZ_LAUNCHED_CHILD=1"); michael@0: michael@0: #if defined(MOZ_WIDGET_ANDROID) michael@0: mozilla::widget::android::GeckoAppShell::ScheduleRestart(); michael@0: #else michael@0: #if defined(XP_MACOSX) michael@0: CommandLineServiceMac::SetupMacCommandLine(gRestartArgc, gRestartArgv, true); michael@0: uint32_t restartMode = 0; michael@0: restartMode = gRestartMode; michael@0: LaunchChildMac(gRestartArgc, gRestartArgv, restartMode); michael@0: #else michael@0: nsCOMPtr lf; michael@0: nsresult rv = XRE_GetBinaryPath(gArgv[0], getter_AddRefs(lf)); michael@0: if (NS_FAILED(rv)) michael@0: return rv; michael@0: michael@0: #if defined(XP_WIN) michael@0: nsAutoString exePath; michael@0: rv = lf->GetPath(exePath); michael@0: if (NS_FAILED(rv)) michael@0: return rv; michael@0: michael@0: if (!WinLaunchChild(exePath.get(), gRestartArgc, gRestartArgv)) michael@0: return NS_ERROR_FAILURE; michael@0: michael@0: #else michael@0: nsAutoCString exePath; michael@0: rv = lf->GetNativePath(exePath); michael@0: if (NS_FAILED(rv)) michael@0: return rv; michael@0: michael@0: #if defined(XP_UNIX) michael@0: if (execv(exePath.get(), gRestartArgv) == -1) michael@0: return NS_ERROR_FAILURE; michael@0: #else michael@0: PRProcess* process = PR_CreateProcess(exePath.get(), gRestartArgv, michael@0: nullptr, nullptr); michael@0: if (!process) return NS_ERROR_FAILURE; michael@0: michael@0: int32_t exitCode; michael@0: PRStatus failed = PR_WaitProcess(process, &exitCode); michael@0: if (failed || exitCode) michael@0: return NS_ERROR_FAILURE; michael@0: #endif // XP_UNIX michael@0: #endif // WP_WIN michael@0: #endif // WP_MACOSX michael@0: #endif // MOZ_WIDGET_ANDROID michael@0: michael@0: return NS_ERROR_LAUNCHED_CHILD_PROCESS; michael@0: } michael@0: michael@0: static const char kProfileProperties[] = michael@0: "chrome://mozapps/locale/profile/profileSelection.properties"; michael@0: michael@0: static nsresult michael@0: ProfileLockedDialog(nsIFile* aProfileDir, nsIFile* aProfileLocalDir, michael@0: nsIProfileUnlocker* aUnlocker, michael@0: nsINativeAppSupport* aNative, nsIProfileLock* *aResult) michael@0: { michael@0: nsresult rv; michael@0: michael@0: ScopedXPCOMStartup xpcom; michael@0: rv = xpcom.Initialize(); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: mozilla::Telemetry::WriteFailedProfileLock(aProfileDir); michael@0: michael@0: rv = xpcom.SetWindowCreator(aNative); michael@0: NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE); michael@0: michael@0: { //extra scoping is needed so we release these components before xpcom shutdown michael@0: nsCOMPtr sbs = michael@0: mozilla::services::GetStringBundleService(); michael@0: NS_ENSURE_TRUE(sbs, NS_ERROR_FAILURE); michael@0: michael@0: nsCOMPtr sb; michael@0: sbs->CreateBundle(kProfileProperties, getter_AddRefs(sb)); michael@0: NS_ENSURE_TRUE_LOG(sbs, NS_ERROR_FAILURE); michael@0: michael@0: NS_ConvertUTF8toUTF16 appName(gAppData->name); michael@0: const char16_t* params[] = {appName.get(), appName.get()}; michael@0: michael@0: nsXPIDLString killMessage; michael@0: #ifndef XP_MACOSX michael@0: 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" michael@0: 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" michael@0: #else michael@0: 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" michael@0: 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" michael@0: #endif michael@0: michael@0: sb->FormatStringFromName(aUnlocker ? kRestartUnlocker : kRestartNoUnlocker, michael@0: params, 2, getter_Copies(killMessage)); michael@0: michael@0: nsXPIDLString killTitle; michael@0: sb->FormatStringFromName(MOZ_UTF16("restartTitle"), michael@0: params, 1, getter_Copies(killTitle)); michael@0: michael@0: if (!killMessage || !killTitle) michael@0: return NS_ERROR_FAILURE; michael@0: michael@0: nsCOMPtr ps michael@0: (do_GetService(NS_PROMPTSERVICE_CONTRACTID)); michael@0: NS_ENSURE_TRUE(ps, NS_ERROR_FAILURE); michael@0: michael@0: if (aUnlocker) { michael@0: int32_t button; michael@0: #ifdef MOZ_WIDGET_ANDROID michael@0: mozilla::widget::android::GeckoAppShell::KillAnyZombies(); michael@0: button = 1; michael@0: #else michael@0: const uint32_t flags = michael@0: (nsIPromptService::BUTTON_TITLE_CANCEL * michael@0: nsIPromptService::BUTTON_POS_0) + michael@0: (nsIPromptService::BUTTON_TITLE_IS_STRING * michael@0: nsIPromptService::BUTTON_POS_1) + michael@0: nsIPromptService::BUTTON_POS_1_DEFAULT; michael@0: michael@0: bool checkState = false; michael@0: rv = ps->ConfirmEx(nullptr, killTitle, killMessage, flags, michael@0: killTitle, nullptr, nullptr, nullptr, michael@0: &checkState, &button); michael@0: NS_ENSURE_SUCCESS_LOG(rv, rv); michael@0: #endif michael@0: michael@0: if (button == 1) { michael@0: rv = aUnlocker->Unlock(nsIProfileUnlocker::FORCE_QUIT); michael@0: if (NS_FAILED(rv)) michael@0: return rv; michael@0: michael@0: return NS_LockProfilePath(aProfileDir, aProfileLocalDir, michael@0: nullptr, aResult); michael@0: } michael@0: } else { michael@0: #ifdef MOZ_WIDGET_ANDROID michael@0: if (mozilla::widget::android::GeckoAppShell::UnlockProfile()) { michael@0: return NS_LockProfilePath(aProfileDir, aProfileLocalDir, michael@0: nullptr, aResult); michael@0: } michael@0: #else michael@0: rv = ps->Alert(nullptr, killTitle, killMessage); michael@0: NS_ENSURE_SUCCESS_LOG(rv, rv); michael@0: #endif michael@0: } michael@0: michael@0: return NS_ERROR_ABORT; michael@0: } michael@0: } michael@0: michael@0: michael@0: static nsresult michael@0: ProfileMissingDialog(nsINativeAppSupport* aNative) michael@0: { michael@0: nsresult rv; michael@0: michael@0: ScopedXPCOMStartup xpcom; michael@0: rv = xpcom.Initialize(); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: rv = xpcom.SetWindowCreator(aNative); michael@0: NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE); michael@0: michael@0: { //extra scoping is needed so we release these components before xpcom shutdown michael@0: nsCOMPtr sbs = michael@0: mozilla::services::GetStringBundleService(); michael@0: NS_ENSURE_TRUE(sbs, NS_ERROR_FAILURE); michael@0: michael@0: nsCOMPtr sb; michael@0: sbs->CreateBundle(kProfileProperties, getter_AddRefs(sb)); michael@0: NS_ENSURE_TRUE_LOG(sbs, NS_ERROR_FAILURE); michael@0: michael@0: NS_ConvertUTF8toUTF16 appName(gAppData->name); michael@0: const char16_t* params[] = {appName.get(), appName.get()}; michael@0: michael@0: nsXPIDLString missingMessage; michael@0: michael@0: // profileMissing michael@0: static const char16_t kMissing[] = {'p','r','o','f','i','l','e','M','i','s','s','i','n','g','\0'}; michael@0: sb->FormatStringFromName(kMissing, params, 2, getter_Copies(missingMessage)); michael@0: michael@0: nsXPIDLString missingTitle; michael@0: sb->FormatStringFromName(MOZ_UTF16("profileMissingTitle"), michael@0: params, 1, getter_Copies(missingTitle)); michael@0: michael@0: if (missingMessage && missingTitle) { michael@0: nsCOMPtr ps michael@0: (do_GetService(NS_PROMPTSERVICE_CONTRACTID)); michael@0: NS_ENSURE_TRUE(ps, NS_ERROR_FAILURE); michael@0: michael@0: ps->Alert(nullptr, missingTitle, missingMessage); michael@0: } michael@0: michael@0: return NS_ERROR_ABORT; michael@0: } michael@0: } michael@0: michael@0: static nsresult michael@0: ProfileLockedDialog(nsIToolkitProfile* aProfile, nsIProfileUnlocker* aUnlocker, michael@0: nsINativeAppSupport* aNative, nsIProfileLock* *aResult) michael@0: { michael@0: nsCOMPtr profileDir; michael@0: nsresult rv = aProfile->GetRootDir(getter_AddRefs(profileDir)); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: bool exists; michael@0: profileDir->Exists(&exists); michael@0: if (!exists) { michael@0: return ProfileMissingDialog(aNative); michael@0: } michael@0: michael@0: nsCOMPtr profileLocalDir; michael@0: rv = aProfile->GetLocalDir(getter_AddRefs(profileLocalDir)); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: return ProfileLockedDialog(profileDir, profileLocalDir, aUnlocker, aNative, michael@0: aResult); michael@0: } michael@0: michael@0: static const char kProfileManagerURL[] = michael@0: "chrome://mozapps/content/profile/profileSelection.xul"; michael@0: michael@0: static nsresult michael@0: ShowProfileManager(nsIToolkitProfileService* aProfileSvc, michael@0: nsINativeAppSupport* aNative) michael@0: { michael@0: if (!CanShowProfileManager()) { michael@0: return NS_ERROR_NOT_IMPLEMENTED; michael@0: } michael@0: michael@0: nsresult rv; michael@0: michael@0: nsCOMPtr profD, profLD; michael@0: char16_t* profileNamePtr; michael@0: nsAutoCString profileName; michael@0: michael@0: { michael@0: ScopedXPCOMStartup xpcom; michael@0: rv = xpcom.Initialize(); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: rv = xpcom.SetWindowCreator(aNative); michael@0: NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE); michael@0: michael@0: #ifdef XP_MACOSX michael@0: CommandLineServiceMac::SetupMacCommandLine(gRestartArgc, gRestartArgv, true); michael@0: #endif michael@0: michael@0: #ifdef XP_WIN michael@0: // we don't have to wait here because profile manager window will pump michael@0: // and DDE message will be handled michael@0: ProcessDDE(aNative, false); michael@0: #endif michael@0: michael@0: { //extra scoping is needed so we release these components before xpcom shutdown michael@0: nsCOMPtr windowWatcher michael@0: (do_GetService(NS_WINDOWWATCHER_CONTRACTID)); michael@0: nsCOMPtr ioParamBlock michael@0: (do_CreateInstance(NS_DIALOGPARAMBLOCK_CONTRACTID)); michael@0: nsCOMPtr dlgArray (do_CreateInstance(NS_ARRAY_CONTRACTID)); michael@0: NS_ENSURE_TRUE(windowWatcher && ioParamBlock && dlgArray, NS_ERROR_FAILURE); michael@0: michael@0: ioParamBlock->SetObjects(dlgArray); michael@0: michael@0: nsCOMPtr appStartup michael@0: (do_GetService(NS_APPSTARTUP_CONTRACTID)); michael@0: NS_ENSURE_TRUE(appStartup, NS_ERROR_FAILURE); michael@0: michael@0: nsCOMPtr newWindow; michael@0: rv = windowWatcher->OpenWindow(nullptr, michael@0: kProfileManagerURL, michael@0: "_blank", michael@0: "centerscreen,chrome,modal,titlebar", michael@0: ioParamBlock, michael@0: getter_AddRefs(newWindow)); michael@0: michael@0: NS_ENSURE_SUCCESS_LOG(rv, rv); michael@0: michael@0: aProfileSvc->Flush(); michael@0: michael@0: int32_t dialogConfirmed; michael@0: rv = ioParamBlock->GetInt(0, &dialogConfirmed); michael@0: if (NS_FAILED(rv) || dialogConfirmed == 0) return NS_ERROR_ABORT; michael@0: michael@0: nsCOMPtr lock; michael@0: rv = dlgArray->QueryElementAt(0, NS_GET_IID(nsIProfileLock), michael@0: getter_AddRefs(lock)); michael@0: NS_ENSURE_SUCCESS_LOG(rv, rv); michael@0: michael@0: rv = lock->GetDirectory(getter_AddRefs(profD)); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: rv = lock->GetLocalDirectory(getter_AddRefs(profLD)); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: rv = ioParamBlock->GetString(0, &profileNamePtr); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: CopyUTF16toUTF8(profileNamePtr, profileName); michael@0: NS_Free(profileNamePtr); michael@0: michael@0: lock->Unlock(); michael@0: } michael@0: } michael@0: michael@0: SaveFileToEnv("XRE_PROFILE_PATH", profD); michael@0: SaveFileToEnv("XRE_PROFILE_LOCAL_PATH", profLD); michael@0: SaveWordToEnv("XRE_PROFILE_NAME", profileName); michael@0: michael@0: bool offline = false; michael@0: aProfileSvc->GetStartOffline(&offline); michael@0: if (offline) { michael@0: SaveToEnv("XRE_START_OFFLINE=1"); michael@0: } michael@0: michael@0: return LaunchChild(aNative); michael@0: } michael@0: michael@0: static nsresult michael@0: GetCurrentProfileIsDefault(nsIToolkitProfileService* aProfileSvc, michael@0: nsIFile* aCurrentProfileRoot, bool *aResult) michael@0: { michael@0: nsresult rv; michael@0: // Check that the profile to reset is the default since reset and the associated migration are michael@0: // only supported in that case. michael@0: nsCOMPtr selectedProfile; michael@0: nsCOMPtr selectedProfileRoot; michael@0: rv = aProfileSvc->GetSelectedProfile(getter_AddRefs(selectedProfile)); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: rv = selectedProfile->GetRootDir(getter_AddRefs(selectedProfileRoot)); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: bool currentIsSelected; michael@0: rv = aCurrentProfileRoot->Equals(selectedProfileRoot, ¤tIsSelected); michael@0: michael@0: *aResult = currentIsSelected; michael@0: return rv; michael@0: } michael@0: michael@0: /** michael@0: * Set the currently running profile as the default/selected one. michael@0: * michael@0: * @param aCurrentProfileRoot The root directory of the current profile. michael@0: * @return an error if aCurrentProfileRoot is not found or the profile could not michael@0: * be set as the default. michael@0: */ michael@0: static nsresult michael@0: SetCurrentProfileAsDefault(nsIToolkitProfileService* aProfileSvc, michael@0: nsIFile* aCurrentProfileRoot) michael@0: { michael@0: NS_ENSURE_ARG_POINTER(aProfileSvc); michael@0: michael@0: nsCOMPtr profiles; michael@0: nsresult rv = aProfileSvc->GetProfiles(getter_AddRefs(profiles)); michael@0: if (NS_FAILED(rv)) michael@0: return rv; michael@0: michael@0: bool foundMatchingProfile = false; michael@0: nsCOMPtr supports; michael@0: rv = profiles->GetNext(getter_AddRefs(supports)); michael@0: while (NS_SUCCEEDED(rv)) { michael@0: nsCOMPtr profile = do_QueryInterface(supports); michael@0: nsCOMPtr profileRoot; michael@0: profile->GetRootDir(getter_AddRefs(profileRoot)); michael@0: profileRoot->Equals(aCurrentProfileRoot, &foundMatchingProfile); michael@0: if (foundMatchingProfile && profile) { michael@0: rv = aProfileSvc->SetSelectedProfile(profile); michael@0: if (NS_SUCCEEDED(rv)) michael@0: rv = aProfileSvc->Flush(); michael@0: return rv; michael@0: } michael@0: rv = profiles->GetNext(getter_AddRefs(supports)); michael@0: } michael@0: return rv; michael@0: } michael@0: michael@0: static bool gDoMigration = false; michael@0: static bool gDoProfileReset = false; michael@0: michael@0: // Pick a profile. We need to end up with a profile lock. michael@0: // michael@0: // 1) check for -profile michael@0: // 2) check for -P michael@0: // 3) check for -ProfileManager michael@0: // 4) use the default profile, if there is one michael@0: // 5) if there are *no* profiles, set up profile-migration michael@0: // 6) display the profile-manager UI michael@0: static nsresult michael@0: SelectProfile(nsIProfileLock* *aResult, nsIToolkitProfileService* aProfileSvc, nsINativeAppSupport* aNative, michael@0: bool* aStartOffline, nsACString* aProfileName) michael@0: { michael@0: StartupTimeline::Record(StartupTimeline::SELECT_PROFILE); michael@0: michael@0: nsresult rv; michael@0: ArgResult ar; michael@0: const char* arg; michael@0: *aResult = nullptr; michael@0: *aStartOffline = false; michael@0: michael@0: ar = CheckArg("offline", true); michael@0: if (ar == ARG_BAD) { michael@0: PR_fprintf(PR_STDERR, "Error: argument -offline is invalid when argument -osint is specified\n"); michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: if (ar || EnvHasValue("XRE_START_OFFLINE")) michael@0: *aStartOffline = true; michael@0: michael@0: if (EnvHasValue("MOZ_RESET_PROFILE_RESTART")) { michael@0: gDoProfileReset = true; michael@0: gDoMigration = true; michael@0: SaveToEnv("MOZ_RESET_PROFILE_RESTART="); michael@0: } michael@0: michael@0: // reset-profile and migration args need to be checked before any profiles are chosen below. michael@0: ar = CheckArg("reset-profile", true); michael@0: if (ar == ARG_BAD) { michael@0: PR_fprintf(PR_STDERR, "Error: argument -reset-profile is invalid when argument -osint is specified\n"); michael@0: return NS_ERROR_FAILURE; michael@0: } else if (ar == ARG_FOUND) { michael@0: gDoProfileReset = true; michael@0: } michael@0: michael@0: ar = CheckArg("migration", true); michael@0: if (ar == ARG_BAD) { michael@0: PR_fprintf(PR_STDERR, "Error: argument -migration is invalid when argument -osint is specified\n"); michael@0: return NS_ERROR_FAILURE; michael@0: } else if (ar == ARG_FOUND) { michael@0: gDoMigration = true; michael@0: } michael@0: michael@0: nsCOMPtr lf = GetFileFromEnv("XRE_PROFILE_PATH"); michael@0: if (lf) { michael@0: nsCOMPtr localDir = michael@0: GetFileFromEnv("XRE_PROFILE_LOCAL_PATH"); michael@0: if (!localDir) { michael@0: localDir = lf; michael@0: } michael@0: michael@0: arg = PR_GetEnv("XRE_PROFILE_NAME"); michael@0: if (arg && *arg && aProfileName) michael@0: aProfileName->Assign(nsDependentCString(arg)); michael@0: michael@0: // Clear out flags that we handled (or should have handled!) last startup. michael@0: const char *dummy; michael@0: CheckArg("p", false, &dummy); michael@0: CheckArg("profile", false, &dummy); michael@0: CheckArg("profilemanager"); michael@0: michael@0: if (gDoProfileReset) { michael@0: // Check that the profile to reset is the default since reset and migration are only michael@0: // supported in that case. michael@0: bool currentIsSelected; michael@0: GetCurrentProfileIsDefault(aProfileSvc, lf, ¤tIsSelected); michael@0: if (!currentIsSelected) { michael@0: NS_WARNING("Profile reset is only supported for the default profile."); michael@0: gDoProfileReset = gDoMigration = false; michael@0: } michael@0: michael@0: // If we're resetting a profile, create a new one and use it to startup. michael@0: nsCOMPtr newProfile; michael@0: rv = CreateResetProfile(aProfileSvc, getter_AddRefs(newProfile)); michael@0: if (NS_SUCCEEDED(rv)) { michael@0: rv = newProfile->GetRootDir(getter_AddRefs(lf)); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: SaveFileToEnv("XRE_PROFILE_PATH", lf); michael@0: michael@0: rv = newProfile->GetLocalDir(getter_AddRefs(localDir)); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: SaveFileToEnv("XRE_PROFILE_LOCAL_PATH", localDir); michael@0: michael@0: rv = newProfile->GetName(*aProfileName); michael@0: if (NS_FAILED(rv)) michael@0: aProfileName->Truncate(0); michael@0: SaveWordToEnv("XRE_PROFILE_NAME", *aProfileName); michael@0: } else { michael@0: NS_WARNING("Profile reset failed."); michael@0: gDoProfileReset = false; michael@0: } michael@0: } michael@0: michael@0: return NS_LockProfilePath(lf, localDir, nullptr, aResult); michael@0: } michael@0: michael@0: ar = CheckArg("profile", true, &arg); michael@0: if (ar == ARG_BAD) { michael@0: PR_fprintf(PR_STDERR, "Error: argument -profile requires a path\n"); michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: if (ar) { michael@0: if (gDoProfileReset) { michael@0: NS_WARNING("Profile reset is only supported for the default profile."); michael@0: gDoProfileReset = false; michael@0: } michael@0: michael@0: nsCOMPtr lf; michael@0: rv = XRE_GetFileFromPath(arg, getter_AddRefs(lf)); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: nsCOMPtr unlocker; michael@0: michael@0: // Check if the profile path exists and it's a directory. michael@0: bool exists; michael@0: lf->Exists(&exists); michael@0: if (!exists) { michael@0: rv = lf->Create(nsIFile::DIRECTORY_TYPE, 0700); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: } michael@0: michael@0: // If a profile path is specified directory on the command line, then michael@0: // assume that the temp directory is the same as the given directory. michael@0: rv = NS_LockProfilePath(lf, lf, getter_AddRefs(unlocker), aResult); michael@0: if (NS_SUCCEEDED(rv)) michael@0: return rv; michael@0: michael@0: return ProfileLockedDialog(lf, lf, unlocker, aNative, aResult); michael@0: } michael@0: michael@0: ar = CheckArg("createprofile", true, &arg); michael@0: if (ar == ARG_BAD) { michael@0: PR_fprintf(PR_STDERR, "Error: argument -createprofile requires a profile name\n"); michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: if (ar) { michael@0: nsCOMPtr profile; michael@0: michael@0: const char* delim = strchr(arg, ' '); michael@0: if (delim) { michael@0: nsCOMPtr lf; michael@0: rv = NS_NewNativeLocalFile(nsDependentCString(delim + 1), michael@0: true, getter_AddRefs(lf)); michael@0: if (NS_FAILED(rv)) { michael@0: PR_fprintf(PR_STDERR, "Error: profile path not valid.\n"); michael@0: return rv; michael@0: } michael@0: michael@0: // As with -profile, assume that the given path will be used for the michael@0: // main profile directory. michael@0: rv = aProfileSvc->CreateProfile(lf, nsDependentCSubstring(arg, delim), michael@0: getter_AddRefs(profile)); michael@0: } else { michael@0: rv = aProfileSvc->CreateProfile(nullptr, nsDependentCString(arg), michael@0: getter_AddRefs(profile)); michael@0: } michael@0: // Some pathological arguments can make it this far michael@0: if (NS_FAILED(rv)) { michael@0: PR_fprintf(PR_STDERR, "Error creating profile.\n"); michael@0: return rv; michael@0: } michael@0: rv = NS_ERROR_ABORT; michael@0: aProfileSvc->Flush(); michael@0: michael@0: // XXXben need to ensure prefs.js exists here so the tinderboxes will michael@0: // not go orange. michael@0: nsCOMPtr prefsJSFile; michael@0: profile->GetRootDir(getter_AddRefs(prefsJSFile)); michael@0: prefsJSFile->AppendNative(NS_LITERAL_CSTRING("prefs.js")); michael@0: nsAutoCString pathStr; michael@0: prefsJSFile->GetNativePath(pathStr); michael@0: PR_fprintf(PR_STDERR, "Success: created profile '%s' at '%s'\n", arg, pathStr.get()); michael@0: bool exists; michael@0: prefsJSFile->Exists(&exists); michael@0: if (!exists) michael@0: prefsJSFile->Create(nsIFile::NORMAL_FILE_TYPE, 0644); michael@0: // XXXdarin perhaps 0600 would be better? michael@0: michael@0: return rv; michael@0: } michael@0: michael@0: uint32_t count; michael@0: rv = aProfileSvc->GetProfileCount(&count); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: ar = CheckArg("p", false, &arg); michael@0: if (ar == ARG_BAD) { michael@0: ar = CheckArg("osint"); michael@0: if (ar == ARG_FOUND) { michael@0: PR_fprintf(PR_STDERR, "Error: argument -p is invalid when argument -osint is specified\n"); michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: if (CanShowProfileManager()) { michael@0: return ShowProfileManager(aProfileSvc, aNative); michael@0: } michael@0: } michael@0: if (ar) { michael@0: ar = CheckArg("osint"); michael@0: if (ar == ARG_FOUND) { michael@0: PR_fprintf(PR_STDERR, "Error: argument -p is invalid when argument -osint is specified\n"); michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: nsCOMPtr profile; michael@0: rv = aProfileSvc->GetProfileByName(nsDependentCString(arg), michael@0: getter_AddRefs(profile)); michael@0: if (NS_SUCCEEDED(rv)) { michael@0: if (gDoProfileReset) { michael@0: NS_WARNING("Profile reset is only supported for the default profile."); michael@0: gDoProfileReset = false; michael@0: } michael@0: michael@0: nsCOMPtr unlocker; michael@0: rv = profile->Lock(getter_AddRefs(unlocker), aResult); michael@0: if (NS_SUCCEEDED(rv)) { michael@0: if (aProfileName) michael@0: aProfileName->Assign(nsDependentCString(arg)); michael@0: return NS_OK; michael@0: } michael@0: michael@0: return ProfileLockedDialog(profile, unlocker, aNative, aResult); michael@0: } michael@0: michael@0: if (CanShowProfileManager()) { michael@0: return ShowProfileManager(aProfileSvc, aNative); michael@0: } michael@0: } michael@0: michael@0: ar = CheckArg("profilemanager", true); michael@0: if (ar == ARG_BAD) { michael@0: PR_fprintf(PR_STDERR, "Error: argument -profilemanager is invalid when argument -osint is specified\n"); michael@0: return NS_ERROR_FAILURE; michael@0: } else if (ar == ARG_FOUND && CanShowProfileManager()) { michael@0: return ShowProfileManager(aProfileSvc, aNative); michael@0: } michael@0: michael@0: if (!count) { michael@0: gDoMigration = true; michael@0: gDoProfileReset = false; michael@0: michael@0: // create a default profile michael@0: nsCOMPtr profile; michael@0: nsresult rv = aProfileSvc->CreateProfile(nullptr, // choose a default dir for us michael@0: NS_LITERAL_CSTRING("default"), michael@0: getter_AddRefs(profile)); michael@0: if (NS_SUCCEEDED(rv)) { michael@0: aProfileSvc->Flush(); michael@0: rv = profile->Lock(nullptr, aResult); michael@0: if (NS_SUCCEEDED(rv)) { michael@0: if (aProfileName) michael@0: aProfileName->Assign(NS_LITERAL_CSTRING("default")); michael@0: return NS_OK; michael@0: } michael@0: } michael@0: } michael@0: michael@0: bool useDefault = true; michael@0: if (count > 1 && CanShowProfileManager()) { michael@0: aProfileSvc->GetStartWithLastProfile(&useDefault); michael@0: } michael@0: michael@0: if (useDefault) { michael@0: nsCOMPtr profile; michael@0: // GetSelectedProfile will auto-select the only profile if there's just one michael@0: aProfileSvc->GetSelectedProfile(getter_AddRefs(profile)); michael@0: if (profile) { michael@0: // If we're resetting a profile, create a new one and use it to startup. michael@0: if (gDoProfileReset) { michael@0: { michael@0: // Check that the source profile is not in use by temporarily acquiring its lock. michael@0: nsIProfileLock* tempProfileLock; michael@0: nsCOMPtr unlocker; michael@0: rv = profile->Lock(getter_AddRefs(unlocker), &tempProfileLock); michael@0: if (NS_FAILED(rv)) michael@0: return ProfileLockedDialog(profile, unlocker, aNative, &tempProfileLock); michael@0: } michael@0: michael@0: nsCOMPtr newProfile; michael@0: rv = CreateResetProfile(aProfileSvc, getter_AddRefs(newProfile)); michael@0: if (NS_SUCCEEDED(rv)) michael@0: profile = newProfile; michael@0: else michael@0: gDoProfileReset = false; michael@0: } michael@0: michael@0: // If you close Firefox and very quickly reopen it, the old Firefox may michael@0: // still be closing down. Rather than immediately showing the michael@0: // "Firefox is running but is not responding" message, we spend a few michael@0: // seconds retrying first. michael@0: michael@0: static const int kLockRetrySeconds = 5; michael@0: static const int kLockRetrySleepMS = 100; michael@0: michael@0: nsCOMPtr unlocker; michael@0: const TimeStamp start = TimeStamp::Now(); michael@0: do { michael@0: rv = profile->Lock(getter_AddRefs(unlocker), aResult); michael@0: if (NS_SUCCEEDED(rv)) { michael@0: StartupTimeline::Record(StartupTimeline::AFTER_PROFILE_LOCKED); michael@0: // Try to grab the profile name. michael@0: if (aProfileName) { michael@0: rv = profile->GetName(*aProfileName); michael@0: if (NS_FAILED(rv)) michael@0: aProfileName->Truncate(0); michael@0: } michael@0: return NS_OK; michael@0: } michael@0: PR_Sleep(kLockRetrySleepMS); michael@0: } while (TimeStamp::Now() - start < TimeDuration::FromSeconds(kLockRetrySeconds)); michael@0: michael@0: return ProfileLockedDialog(profile, unlocker, aNative, aResult); michael@0: } michael@0: } michael@0: michael@0: if (!CanShowProfileManager()) { michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: return ShowProfileManager(aProfileSvc, aNative); michael@0: } michael@0: michael@0: /** michael@0: * Checks the compatibility.ini file to see if we have updated our application michael@0: * or otherwise invalidated our caches. If the application has been updated, michael@0: * we return false; otherwise, we return true. We also write the status michael@0: * of the caches (valid/invalid) into the return param aCachesOK. The aCachesOK michael@0: * is always invalid if the application has been updated. michael@0: */ michael@0: static bool michael@0: CheckCompatibility(nsIFile* aProfileDir, const nsCString& aVersion, michael@0: const nsCString& aOSABI, nsIFile* aXULRunnerDir, michael@0: nsIFile* aAppDir, nsIFile* aFlagFile, michael@0: bool* aCachesOK) michael@0: { michael@0: *aCachesOK = false; michael@0: nsCOMPtr file; michael@0: aProfileDir->Clone(getter_AddRefs(file)); michael@0: if (!file) michael@0: return false; michael@0: file->AppendNative(FILE_COMPATIBILITY_INFO); michael@0: michael@0: nsINIParser parser; michael@0: nsresult rv = parser.Init(file); michael@0: if (NS_FAILED(rv)) michael@0: return false; michael@0: michael@0: nsAutoCString buf; michael@0: rv = parser.GetString("Compatibility", "LastVersion", buf); michael@0: if (NS_FAILED(rv) || !aVersion.Equals(buf)) michael@0: return false; michael@0: michael@0: rv = parser.GetString("Compatibility", "LastOSABI", buf); michael@0: if (NS_FAILED(rv) || !aOSABI.Equals(buf)) michael@0: return false; michael@0: michael@0: rv = parser.GetString("Compatibility", "LastPlatformDir", buf); michael@0: if (NS_FAILED(rv)) michael@0: return false; michael@0: michael@0: nsCOMPtr lf; michael@0: rv = NS_NewNativeLocalFile(buf, false, michael@0: getter_AddRefs(lf)); michael@0: if (NS_FAILED(rv)) michael@0: return false; michael@0: michael@0: bool eq; michael@0: rv = lf->Equals(aXULRunnerDir, &eq); michael@0: if (NS_FAILED(rv) || !eq) michael@0: return false; michael@0: michael@0: if (aAppDir) { michael@0: rv = parser.GetString("Compatibility", "LastAppDir", buf); michael@0: if (NS_FAILED(rv)) michael@0: return false; michael@0: michael@0: rv = NS_NewNativeLocalFile(buf, false, michael@0: getter_AddRefs(lf)); michael@0: if (NS_FAILED(rv)) michael@0: return false; michael@0: michael@0: rv = lf->Equals(aAppDir, &eq); michael@0: if (NS_FAILED(rv) || !eq) michael@0: return false; michael@0: } michael@0: michael@0: // If we see this flag, caches are invalid. michael@0: rv = parser.GetString("Compatibility", "InvalidateCaches", buf); michael@0: *aCachesOK = (NS_FAILED(rv) || !buf.EqualsLiteral("1")); michael@0: michael@0: bool purgeCaches = false; michael@0: if (aFlagFile) { michael@0: aFlagFile->Exists(&purgeCaches); michael@0: } michael@0: michael@0: *aCachesOK = !purgeCaches && *aCachesOK; michael@0: return true; michael@0: } michael@0: michael@0: static void BuildVersion(nsCString &aBuf) michael@0: { michael@0: aBuf.Assign(gAppData->version); michael@0: aBuf.Append('_'); michael@0: aBuf.Append(gAppData->buildID); michael@0: aBuf.Append('/'); michael@0: aBuf.Append(gToolkitBuildID); michael@0: } michael@0: michael@0: static void michael@0: WriteVersion(nsIFile* aProfileDir, const nsCString& aVersion, michael@0: const nsCString& aOSABI, nsIFile* aXULRunnerDir, michael@0: nsIFile* aAppDir, bool invalidateCache) michael@0: { michael@0: nsCOMPtr file; michael@0: aProfileDir->Clone(getter_AddRefs(file)); michael@0: if (!file) michael@0: return; michael@0: file->AppendNative(FILE_COMPATIBILITY_INFO); michael@0: michael@0: nsAutoCString platformDir; michael@0: aXULRunnerDir->GetNativePath(platformDir); michael@0: michael@0: nsAutoCString appDir; michael@0: if (aAppDir) michael@0: aAppDir->GetNativePath(appDir); michael@0: michael@0: PRFileDesc *fd = nullptr; michael@0: file->OpenNSPRFileDesc(PR_WRONLY | PR_CREATE_FILE | PR_TRUNCATE, 0600, &fd); michael@0: if (!fd) { michael@0: NS_ERROR("could not create output stream"); michael@0: return; michael@0: } michael@0: michael@0: static const char kHeader[] = "[Compatibility]" NS_LINEBREAK michael@0: "LastVersion="; michael@0: michael@0: PR_Write(fd, kHeader, sizeof(kHeader) - 1); michael@0: PR_Write(fd, aVersion.get(), aVersion.Length()); michael@0: michael@0: static const char kOSABIHeader[] = NS_LINEBREAK "LastOSABI="; michael@0: PR_Write(fd, kOSABIHeader, sizeof(kOSABIHeader) - 1); michael@0: PR_Write(fd, aOSABI.get(), aOSABI.Length()); michael@0: michael@0: static const char kPlatformDirHeader[] = NS_LINEBREAK "LastPlatformDir="; michael@0: michael@0: PR_Write(fd, kPlatformDirHeader, sizeof(kPlatformDirHeader) - 1); michael@0: PR_Write(fd, platformDir.get(), platformDir.Length()); michael@0: michael@0: static const char kAppDirHeader[] = NS_LINEBREAK "LastAppDir="; michael@0: if (aAppDir) { michael@0: PR_Write(fd, kAppDirHeader, sizeof(kAppDirHeader) - 1); michael@0: PR_Write(fd, appDir.get(), appDir.Length()); michael@0: } michael@0: michael@0: static const char kInvalidationHeader[] = NS_LINEBREAK "InvalidateCaches=1"; michael@0: if (invalidateCache) michael@0: PR_Write(fd, kInvalidationHeader, sizeof(kInvalidationHeader) - 1); michael@0: michael@0: static const char kNL[] = NS_LINEBREAK; michael@0: PR_Write(fd, kNL, sizeof(kNL) - 1); michael@0: michael@0: PR_Close(fd); michael@0: } michael@0: michael@0: /** michael@0: * Returns true if the startup cache file was successfully removed. michael@0: * Returns false if file->Clone fails at any point (OOM) or if unable michael@0: * to remove the startup cache file. Note in particular the return value michael@0: * is unaffected by a failure to remove extensions.ini michael@0: */ michael@0: static bool michael@0: RemoveComponentRegistries(nsIFile* aProfileDir, nsIFile* aLocalProfileDir, michael@0: bool aRemoveEMFiles) michael@0: { michael@0: nsCOMPtr file; michael@0: aProfileDir->Clone(getter_AddRefs(file)); michael@0: if (!file) michael@0: return false; michael@0: michael@0: if (aRemoveEMFiles) { michael@0: file->SetNativeLeafName(NS_LITERAL_CSTRING("extensions.ini")); michael@0: file->Remove(false); michael@0: } michael@0: michael@0: aLocalProfileDir->Clone(getter_AddRefs(file)); michael@0: if (!file) michael@0: return false; michael@0: michael@0: #if defined(XP_UNIX) || defined(XP_BEOS) michael@0: #define PLATFORM_FASL_SUFFIX ".mfasl" michael@0: #elif defined(XP_WIN) michael@0: #define PLATFORM_FASL_SUFFIX ".mfl" michael@0: #endif michael@0: michael@0: file->AppendNative(NS_LITERAL_CSTRING("XUL" PLATFORM_FASL_SUFFIX)); michael@0: file->Remove(false); michael@0: michael@0: file->SetNativeLeafName(NS_LITERAL_CSTRING("XPC" PLATFORM_FASL_SUFFIX)); michael@0: file->Remove(false); michael@0: michael@0: file->SetNativeLeafName(NS_LITERAL_CSTRING("startupCache")); michael@0: nsresult rv = file->Remove(true); michael@0: return NS_SUCCEEDED(rv) || rv == NS_ERROR_FILE_TARGET_DOES_NOT_EXIST; michael@0: } michael@0: michael@0: // To support application initiated restart via nsIAppStartup.quit, we michael@0: // need to save various environment variables, and then restore them michael@0: // before re-launching the application. michael@0: michael@0: static struct SavedVar { michael@0: const char *name; michael@0: char *value; michael@0: } gSavedVars[] = { michael@0: {"XUL_APP_FILE", nullptr} michael@0: }; michael@0: michael@0: static void SaveStateForAppInitiatedRestart() michael@0: { michael@0: for (size_t i = 0; i < ArrayLength(gSavedVars); ++i) { michael@0: const char *s = PR_GetEnv(gSavedVars[i].name); michael@0: if (s) michael@0: gSavedVars[i].value = PR_smprintf("%s=%s", gSavedVars[i].name, s); michael@0: } michael@0: } michael@0: michael@0: static void RestoreStateForAppInitiatedRestart() michael@0: { michael@0: for (size_t i = 0; i < ArrayLength(gSavedVars); ++i) { michael@0: if (gSavedVars[i].value) michael@0: PR_SetEnv(gSavedVars[i].value); michael@0: } michael@0: } michael@0: michael@0: #ifdef MOZ_CRASHREPORTER michael@0: // When we first initialize the crash reporter we don't have a profile, michael@0: // so we set the minidump path to $TEMP. Once we have a profile, michael@0: // we set it to $PROFILE/minidumps, creating the directory michael@0: // if needed. michael@0: static void MakeOrSetMinidumpPath(nsIFile* profD) michael@0: { michael@0: nsCOMPtr dumpD; michael@0: profD->Clone(getter_AddRefs(dumpD)); michael@0: michael@0: if(dumpD) { michael@0: bool fileExists; michael@0: //XXX: do some more error checking here michael@0: dumpD->Append(NS_LITERAL_STRING("minidumps")); michael@0: dumpD->Exists(&fileExists); michael@0: if(!fileExists) { michael@0: dumpD->Create(nsIFile::DIRECTORY_TYPE, 0700); michael@0: } michael@0: michael@0: nsAutoString pathStr; michael@0: if(NS_SUCCEEDED(dumpD->GetPath(pathStr))) michael@0: CrashReporter::SetMinidumpPath(pathStr); michael@0: } michael@0: } michael@0: #endif michael@0: michael@0: const nsXREAppData* gAppData = nullptr; michael@0: michael@0: #ifdef MOZ_WIDGET_GTK michael@0: #include "prlink.h" michael@0: typedef void (*_g_set_application_name_fn)(const gchar *application_name); michael@0: typedef void (*_gtk_window_set_auto_startup_notification_fn)(gboolean setting); michael@0: michael@0: static PRFuncPtr FindFunction(const char* aName) michael@0: { michael@0: PRLibrary *lib = nullptr; michael@0: PRFuncPtr result = PR_FindFunctionSymbolAndLibrary(aName, &lib); michael@0: // Since the library was already loaded, we can safely unload it here. michael@0: if (lib) { michael@0: PR_UnloadLibrary(lib); michael@0: } michael@0: return result; michael@0: } michael@0: michael@0: static void MOZ_gdk_display_close(GdkDisplay *display) michael@0: { michael@0: #if CLEANUP_MEMORY michael@0: // XXX wallpaper for bug 417163: don't close the Display if we're using the michael@0: // Qt theme because we crash (in Qt code) when using jemalloc. michael@0: bool theme_is_qt = false; michael@0: GtkSettings* settings = michael@0: gtk_settings_get_for_screen(gdk_display_get_default_screen(display)); michael@0: gchar *theme_name; michael@0: g_object_get(settings, "gtk-theme-name", &theme_name, nullptr); michael@0: if (theme_name) { michael@0: theme_is_qt = strcmp(theme_name, "Qt") == 0; michael@0: if (theme_is_qt) michael@0: NS_WARNING("wallpaper bug 417163 for Qt theme"); michael@0: g_free(theme_name); michael@0: } michael@0: michael@0: // Get a (new) Pango context that holds a reference to the fontmap that michael@0: // GTK has been using. gdk_pango_context_get() must be called while GTK michael@0: // has a default display. michael@0: PangoContext *pangoContext = gdk_pango_context_get(); michael@0: michael@0: bool buggyCairoShutdown = cairo_version() < CAIRO_VERSION_ENCODE(1, 4, 0); michael@0: michael@0: if (!buggyCairoShutdown) { michael@0: // We should shut down GDK before we shut down libraries it depends on michael@0: // like Pango and cairo. But if cairo shutdown is buggy, we should michael@0: // shut down cairo first otherwise it may crash because of dangling michael@0: // references to Display objects (see bug 469831). michael@0: if (!theme_is_qt) michael@0: gdk_display_close(display); michael@0: } michael@0: michael@0: // Clean up PangoCairo's default fontmap. michael@0: // This pango_fc_font_map_shutdown call (and the associated code to michael@0: // get the font map) really shouldn't be needed anymore, except that michael@0: // it's needed to avoid having cairo_debug_reset_static_data fatally michael@0: // assert if we've leaked other things that hold on to the fontmap, michael@0: // which is something that currently happens in mochitest-plugins. michael@0: // Even if it didn't happen in mochitest-plugins, we probably want to michael@0: // avoid the crash-on-leak problem since it makes it harder to use michael@0: // many of our leak tools to debug leaks. michael@0: michael@0: // This doesn't take a reference. michael@0: PangoFontMap *fontmap = pango_context_get_font_map(pangoContext); michael@0: // Do some shutdown of the fontmap, which releases the fonts, clearing a michael@0: // bunch of circular references from the fontmap through the fonts back to michael@0: // itself. The shutdown that this does is much less than what's done by michael@0: // the fontmap's finalize, though. michael@0: if (PANGO_IS_FC_FONT_MAP(fontmap)) michael@0: pango_fc_font_map_shutdown(PANGO_FC_FONT_MAP(fontmap)); michael@0: g_object_unref(pangoContext); michael@0: michael@0: // Tell PangoCairo to release its default fontmap. michael@0: pango_cairo_font_map_set_default(nullptr); michael@0: michael@0: // cairo_debug_reset_static_data() is prototyped through cairo.h included michael@0: // by gtk.h. michael@0: #ifdef cairo_debug_reset_static_data michael@0: #error "Looks like we're including Mozilla's cairo instead of system cairo" michael@0: #endif michael@0: cairo_debug_reset_static_data(); michael@0: // FIXME: Do we need to call this in non-GTK2 cases as well? michael@0: FcFini(); michael@0: michael@0: if (buggyCairoShutdown) { michael@0: if (!theme_is_qt) michael@0: gdk_display_close(display); michael@0: } michael@0: #else // not CLEANUP_MEMORY michael@0: // Don't do anything to avoid running into driver bugs under XCloseDisplay(). michael@0: // See bug 973192. michael@0: (void) display; michael@0: #endif michael@0: } michael@0: #endif // MOZ_WIDGET_GTK2 michael@0: michael@0: /** michael@0: * NSPR will search for the "nspr_use_zone_allocator" symbol throughout michael@0: * the process and use it to determine whether the application defines its own michael@0: * memory allocator or not. michael@0: * michael@0: * Since most applications (e.g. Firefox and Thunderbird) don't use any special michael@0: * allocators and therefore don't define this symbol, NSPR must search the michael@0: * entire process, which reduces startup performance. michael@0: * michael@0: * By defining the symbol here, we can avoid the wasted lookup and hopefully michael@0: * improve startup performance. michael@0: */ michael@0: NS_VISIBILITY_DEFAULT PRBool nspr_use_zone_allocator = PR_FALSE; michael@0: michael@0: #ifdef CAIRO_HAS_DWRITE_FONT michael@0: michael@0: #include michael@0: michael@0: #ifdef DEBUG_DWRITE_STARTUP michael@0: michael@0: #define LOGREGISTRY(msg) LogRegistryEvent(msg) michael@0: michael@0: // for use when monitoring process michael@0: static void LogRegistryEvent(const wchar_t *msg) michael@0: { michael@0: HKEY dummyKey; michael@0: HRESULT hr; michael@0: wchar_t buf[512]; michael@0: michael@0: wsprintf(buf, L" log %s", msg); michael@0: hr = RegOpenKeyEx(HKEY_LOCAL_MACHINE, buf, 0, KEY_READ, &dummyKey); michael@0: if (SUCCEEDED(hr)) { michael@0: RegCloseKey(dummyKey); michael@0: } michael@0: } michael@0: #else michael@0: michael@0: #define LOGREGISTRY(msg) michael@0: michael@0: #endif michael@0: michael@0: static DWORD InitDwriteBG(LPVOID lpdwThreadParam) michael@0: { michael@0: SetThreadPriority(GetCurrentThread(), THREAD_MODE_BACKGROUND_BEGIN); michael@0: LOGREGISTRY(L"loading dwrite.dll"); michael@0: HMODULE dwdll = LoadLibraryW(L"dwrite.dll"); michael@0: if (dwdll) { michael@0: decltype(DWriteCreateFactory)* createDWriteFactory = (decltype(DWriteCreateFactory)*) michael@0: GetProcAddress(dwdll, "DWriteCreateFactory"); michael@0: if (createDWriteFactory) { michael@0: LOGREGISTRY(L"creating dwrite factory"); michael@0: IDWriteFactory *factory; michael@0: HRESULT hr = createDWriteFactory( michael@0: DWRITE_FACTORY_TYPE_SHARED, michael@0: __uuidof(IDWriteFactory), michael@0: reinterpret_cast(&factory)); michael@0: if (SUCCEEDED(hr)) { michael@0: LOGREGISTRY(L"dwrite factory done"); michael@0: factory->Release(); michael@0: LOGREGISTRY(L"freed factory"); michael@0: } else { michael@0: LOGREGISTRY(L"failed to create factory"); michael@0: } michael@0: } michael@0: } michael@0: SetThreadPriority(GetCurrentThread(), THREAD_MODE_BACKGROUND_END); michael@0: return 0; michael@0: } michael@0: #endif michael@0: michael@0: #ifdef USE_GLX_TEST michael@0: bool fire_glxtest_process(); michael@0: #endif michael@0: michael@0: #if defined(XP_WIN) && defined(MOZ_METRO) michael@0: #ifndef AHE_TYPE michael@0: enum AHE_TYPE { michael@0: AHE_DESKTOP = 0, michael@0: AHE_IMMERSIVE = 1 michael@0: }; michael@0: #endif michael@0: michael@0: /* michael@0: * The Windows launcher uses this value to decide what front end ui michael@0: * to launch. We always launch the same ui unless the user michael@0: * specifically asks to switch. Update the value on every startup michael@0: * based on the environment requested. michael@0: */ michael@0: void michael@0: SetLastWinRunType(AHE_TYPE aType) michael@0: { michael@0: HKEY key; michael@0: LONG result = RegOpenKeyExW(HKEY_CURRENT_USER, michael@0: L"SOFTWARE\\Mozilla\\Firefox", michael@0: 0, KEY_WRITE, &key); michael@0: if (result != ERROR_SUCCESS) { michael@0: return; michael@0: } michael@0: DWORD value = (DWORD)aType; michael@0: result = RegSetValueEx(key, L"MetroLastAHE", 0, REG_DWORD, michael@0: reinterpret_cast(&value), michael@0: sizeof(DWORD)); michael@0: RegCloseKey(key); michael@0: } michael@0: #endif // defined(XP_WIN) && defined(MOZ_METRO) michael@0: michael@0: #include "GeckoProfiler.h" michael@0: michael@0: // Encapsulates startup and shutdown state for XRE_main michael@0: class XREMain michael@0: { michael@0: public: michael@0: XREMain() : michael@0: mScopedXPCom(nullptr) michael@0: , mAppData(nullptr) michael@0: , mStartOffline(false) michael@0: , mShuttingDown(false) michael@0: #ifdef MOZ_ENABLE_XREMOTE michael@0: , mDisableRemote(false) michael@0: #endif michael@0: #if defined(MOZ_WIDGET_GTK) michael@0: , mGdkDisplay(nullptr) michael@0: #endif michael@0: {}; michael@0: michael@0: ~XREMain() { michael@0: if (mAppData) { michael@0: delete mAppData; michael@0: } michael@0: if (mScopedXPCom) { michael@0: NS_WARNING("Scoped xpcom should have been deleted!"); michael@0: delete mScopedXPCom; michael@0: } michael@0: } michael@0: michael@0: int XRE_main(int argc, char* argv[], const nsXREAppData* aAppData); michael@0: int XRE_mainInit(bool* aExitFlag); michael@0: int XRE_mainStartup(bool* aExitFlag); michael@0: nsresult XRE_mainRun(); michael@0: michael@0: nsCOMPtr mNativeApp; michael@0: nsCOMPtr mProfileSvc; michael@0: nsCOMPtr mProfD; michael@0: nsCOMPtr mProfLD; michael@0: nsCOMPtr mProfileLock; michael@0: #ifdef MOZ_ENABLE_XREMOTE michael@0: nsCOMPtr mRemoteService; michael@0: #endif michael@0: michael@0: ScopedXPCOMStartup* mScopedXPCom; michael@0: ScopedAppData* mAppData; michael@0: nsXREDirProvider mDirProvider; michael@0: nsAutoCString mProfileName; michael@0: nsAutoCString mDesktopStartupID; michael@0: michael@0: bool mStartOffline; michael@0: bool mShuttingDown; michael@0: #ifdef MOZ_ENABLE_XREMOTE michael@0: bool mDisableRemote; michael@0: #endif michael@0: michael@0: #if defined(MOZ_WIDGET_GTK) michael@0: GdkDisplay* mGdkDisplay; michael@0: #endif michael@0: }; michael@0: michael@0: /* michael@0: * XRE_mainInit - Initial setup and command line parameter processing. michael@0: * Main() will exit early if either return value != 0 or if aExitFlag is michael@0: * true. michael@0: */ michael@0: int michael@0: XREMain::XRE_mainInit(bool* aExitFlag) michael@0: { michael@0: if (!aExitFlag) michael@0: return 1; michael@0: *aExitFlag = false; michael@0: michael@0: StartupTimeline::Record(StartupTimeline::MAIN); michael@0: michael@0: nsresult rv; michael@0: ArgResult ar; michael@0: michael@0: #ifdef DEBUG michael@0: if (PR_GetEnv("XRE_MAIN_BREAK")) michael@0: NS_BREAK(); michael@0: #endif michael@0: michael@0: #ifdef USE_GLX_TEST michael@0: // bug 639842 - it's very important to fire this process BEFORE we set up michael@0: // error handling. indeed, this process is expected to be crashy, and we michael@0: // don't want the user to see its crashes. That's the whole reason for michael@0: // doing this in a separate process. michael@0: // michael@0: // This call will cause a fork and the fork will terminate itself separately michael@0: // from the usual shutdown sequence michael@0: fire_glxtest_process(); michael@0: #endif michael@0: michael@0: #if defined(XP_WIN) && defined(MOZ_METRO) michael@0: // Don't remove this arg, we want to pass it on to nsUpdateDriver michael@0: if (CheckArg("metro-update", false, nullptr, false) == ARG_FOUND || michael@0: XRE_GetWindowsEnvironment() == WindowsEnvironmentType_Metro) { michael@0: // If we're doing a restart update that was initiated from metro land, michael@0: // we'll be running desktop to handle the actual update. Request that michael@0: // after the restart we launch into metro. michael@0: SetLastWinRunType(AHE_IMMERSIVE); michael@0: } else { michael@0: SetLastWinRunType(AHE_DESKTOP); michael@0: } michael@0: #endif michael@0: michael@0: SetupErrorHandling(gArgv[0]); michael@0: michael@0: #ifdef CAIRO_HAS_DWRITE_FONT michael@0: { michael@0: // Bug 602792 - when DWriteCreateFactory is called the dwrite client dll michael@0: // starts the FntCache service if it isn't already running (it's set michael@0: // to manual startup by default in Windows 7 RTM). Subsequent DirectWrite michael@0: // calls cause the IDWriteFactory object to communicate with the FntCache michael@0: // service with a timeout; if there's no response after the timeout, the michael@0: // DirectWrite client library will assume the service isn't around and do michael@0: // manual font file I/O on _all_ system fonts. To avoid this, load the michael@0: // dwrite library and create a factory as early as possible so that the michael@0: // FntCache service is ready by the time it's needed. michael@0: michael@0: if (IsVistaOrLater()) { michael@0: CreateThread(nullptr, 0, (LPTHREAD_START_ROUTINE)&InitDwriteBG, michael@0: nullptr, 0, nullptr); michael@0: } michael@0: } michael@0: #endif michael@0: michael@0: #ifdef XP_UNIX michael@0: const char *home = PR_GetEnv("HOME"); michael@0: if (!home || !*home) { michael@0: struct passwd *pw = getpwuid(geteuid()); michael@0: if (!pw || !pw->pw_dir) { michael@0: Output(true, "Could not determine HOME directory"); michael@0: return 1; michael@0: } michael@0: SaveWordToEnv("HOME", nsDependentCString(pw->pw_dir)); michael@0: } michael@0: #endif michael@0: michael@0: #ifdef MOZ_ACCESSIBILITY_ATK michael@0: // Suppress atk-bridge init at startup, until mozilla accessibility is michael@0: // initialized. This works after gnome 2.24.2. michael@0: SaveToEnv("NO_AT_BRIDGE=1"); michael@0: #endif michael@0: michael@0: // Check for application.ini overrides michael@0: const char* override = nullptr; michael@0: ar = CheckArg("override", true, &override); michael@0: if (ar == ARG_BAD) { michael@0: Output(true, "Incorrect number of arguments passed to -override"); michael@0: return 1; michael@0: } michael@0: else if (ar == ARG_FOUND) { michael@0: nsCOMPtr overrideLF; michael@0: rv = XRE_GetFileFromPath(override, getter_AddRefs(overrideLF)); michael@0: if (NS_FAILED(rv)) { michael@0: Output(true, "Error: unrecognized override.ini path.\n"); michael@0: return 1; michael@0: } michael@0: michael@0: rv = XRE_ParseAppData(overrideLF, mAppData); michael@0: if (NS_FAILED(rv)) { michael@0: Output(true, "Couldn't read override.ini"); michael@0: return 1; michael@0: } michael@0: } michael@0: michael@0: // Check sanity and correctness of app data. michael@0: michael@0: if (!mAppData->name) { michael@0: Output(true, "Error: App:Name not specified in application.ini\n"); michael@0: return 1; michael@0: } michael@0: if (!mAppData->buildID) { michael@0: Output(true, "Error: App:BuildID not specified in application.ini\n"); michael@0: return 1; michael@0: } michael@0: michael@0: // XXX Originally ScopedLogging was here? Now it's in XRE_main above michael@0: // XRE_mainInit. michael@0: michael@0: if (!mAppData->xreDirectory) { michael@0: nsCOMPtr lf; michael@0: rv = XRE_GetBinaryPath(gArgv[0], getter_AddRefs(lf)); michael@0: if (NS_FAILED(rv)) michael@0: return 2; michael@0: michael@0: nsCOMPtr greDir; michael@0: rv = lf->GetParent(getter_AddRefs(greDir)); michael@0: if (NS_FAILED(rv)) michael@0: return 2; michael@0: michael@0: greDir.forget(&mAppData->xreDirectory); michael@0: } michael@0: michael@0: if (!mAppData->directory) { michael@0: NS_IF_ADDREF(mAppData->directory = mAppData->xreDirectory); michael@0: } michael@0: michael@0: if (mAppData->size > offsetof(nsXREAppData, minVersion)) { michael@0: if (!mAppData->minVersion) { michael@0: Output(true, "Error: Gecko:MinVersion not specified in application.ini\n"); michael@0: return 1; michael@0: } michael@0: michael@0: if (!mAppData->maxVersion) { michael@0: // If no maxVersion is specified, we assume the app is only compatible michael@0: // with the initial preview release. Do not increment this number ever! michael@0: SetAllocatedString(mAppData->maxVersion, "1.*"); michael@0: } michael@0: michael@0: if (mozilla::Version(mAppData->minVersion) > gToolkitVersion || michael@0: mozilla::Version(mAppData->maxVersion) < gToolkitVersion) { michael@0: Output(true, "Error: Platform version '%s' is not compatible with\n" michael@0: "minVersion >= %s\nmaxVersion <= %s\n", michael@0: gToolkitVersion, michael@0: mAppData->minVersion, mAppData->maxVersion); michael@0: return 1; michael@0: } michael@0: } michael@0: michael@0: rv = mDirProvider.Initialize(mAppData->directory, mAppData->xreDirectory); michael@0: if (NS_FAILED(rv)) michael@0: return 1; michael@0: michael@0: #ifdef MOZ_CRASHREPORTER michael@0: if (EnvHasValue("MOZ_CRASHREPORTER")) { michael@0: mAppData->flags |= NS_XRE_ENABLE_CRASH_REPORTER; michael@0: } michael@0: michael@0: if ((mAppData->flags & NS_XRE_ENABLE_CRASH_REPORTER) && michael@0: NS_SUCCEEDED( michael@0: CrashReporter::SetExceptionHandler(mAppData->xreDirectory))) { michael@0: CrashReporter::UpdateCrashEventsDir(); michael@0: if (mAppData->crashReporterURL) michael@0: CrashReporter::SetServerURL(nsDependentCString(mAppData->crashReporterURL)); michael@0: michael@0: // pass some basic info from the app data michael@0: if (mAppData->vendor) michael@0: CrashReporter::AnnotateCrashReport(NS_LITERAL_CSTRING("Vendor"), michael@0: nsDependentCString(mAppData->vendor)); michael@0: if (mAppData->name) michael@0: CrashReporter::AnnotateCrashReport(NS_LITERAL_CSTRING("ProductName"), michael@0: nsDependentCString(mAppData->name)); michael@0: if (mAppData->ID) michael@0: CrashReporter::AnnotateCrashReport(NS_LITERAL_CSTRING("ProductID"), michael@0: nsDependentCString(mAppData->ID)); michael@0: if (mAppData->version) michael@0: CrashReporter::AnnotateCrashReport(NS_LITERAL_CSTRING("Version"), michael@0: nsDependentCString(mAppData->version)); michael@0: if (mAppData->buildID) michael@0: CrashReporter::AnnotateCrashReport(NS_LITERAL_CSTRING("BuildID"), michael@0: nsDependentCString(mAppData->buildID)); michael@0: michael@0: nsDependentCString releaseChannel(NS_STRINGIFY(MOZ_UPDATE_CHANNEL)); michael@0: CrashReporter::AnnotateCrashReport(NS_LITERAL_CSTRING("ReleaseChannel"), michael@0: releaseChannel); michael@0: #ifdef MOZ_LINKER michael@0: CrashReporter::AnnotateCrashReport(NS_LITERAL_CSTRING("CrashAddressLikelyWrong"), michael@0: IsSignalHandlingBroken() ? NS_LITERAL_CSTRING("1") michael@0: : NS_LITERAL_CSTRING("0")); michael@0: #endif michael@0: CrashReporter::SetRestartArgs(gArgc, gArgv); michael@0: michael@0: // annotate other data (user id etc) michael@0: nsCOMPtr userAppDataDir; michael@0: if (NS_SUCCEEDED(mDirProvider.GetUserAppDataDirectory( michael@0: getter_AddRefs(userAppDataDir)))) { michael@0: CrashReporter::SetupExtraData(userAppDataDir, michael@0: nsDependentCString(mAppData->buildID)); michael@0: michael@0: // see if we have a crashreporter-override.ini in the application directory michael@0: nsCOMPtr overrideini; michael@0: bool exists; michael@0: if (NS_SUCCEEDED(mDirProvider.GetAppDir()->Clone(getter_AddRefs(overrideini))) && michael@0: NS_SUCCEEDED(overrideini->AppendNative(NS_LITERAL_CSTRING("crashreporter-override.ini"))) && michael@0: NS_SUCCEEDED(overrideini->Exists(&exists)) && michael@0: exists) { michael@0: #ifdef XP_WIN michael@0: nsAutoString overridePathW; michael@0: overrideini->GetPath(overridePathW); michael@0: NS_ConvertUTF16toUTF8 overridePath(overridePathW); michael@0: #else michael@0: nsAutoCString overridePath; michael@0: overrideini->GetNativePath(overridePath); michael@0: #endif michael@0: michael@0: SaveWordToEnv("MOZ_CRASHREPORTER_STRINGS_OVERRIDE", overridePath); michael@0: } michael@0: } michael@0: } michael@0: #endif michael@0: michael@0: #ifdef XP_MACOSX michael@0: if (EnvHasValue("MOZ_LAUNCHED_CHILD")) { michael@0: // This is needed, on relaunch, to force the OS to use the "Cocoa Dock michael@0: // API". Otherwise the call to ReceiveNextEvent() below will make it michael@0: // use the "Carbon Dock API". For more info see bmo bug 377166. michael@0: EnsureUseCocoaDockAPI(); michael@0: michael@0: // When the app relaunches, the original process exits. This causes michael@0: // the dock tile to stop bouncing, lose the "running" triangle, and michael@0: // if the tile does not permanently reside in the Dock, even disappear. michael@0: // This can be confusing to the user, who is expecting the app to launch. michael@0: // Calling ReceiveNextEvent without requesting any event is enough to michael@0: // cause a dock tile for the child process to appear. michael@0: const EventTypeSpec kFakeEventList[] = { { INT_MAX, INT_MAX } }; michael@0: EventRef event; michael@0: ::ReceiveNextEvent(GetEventTypeCount(kFakeEventList), kFakeEventList, michael@0: kEventDurationNoWait, false, &event); michael@0: } michael@0: michael@0: if (CheckArg("foreground")) { michael@0: // The original process communicates that it was in the foreground by michael@0: // adding this argument. This new process, which is taking over for michael@0: // the old one, should make itself the active application. michael@0: ProcessSerialNumber psn; michael@0: if (::GetCurrentProcess(&psn) == noErr) michael@0: ::SetFrontProcess(&psn); michael@0: } michael@0: #endif michael@0: michael@0: SaveToEnv("MOZ_LAUNCHED_CHILD="); michael@0: michael@0: gRestartArgc = gArgc; michael@0: gRestartArgv = (char**) malloc(sizeof(char*) * (gArgc + 1 + (override ? 2 : 0))); michael@0: if (!gRestartArgv) { michael@0: return 1; michael@0: } michael@0: michael@0: int i; michael@0: for (i = 0; i < gArgc; ++i) { michael@0: gRestartArgv[i] = gArgv[i]; michael@0: } michael@0: michael@0: // Add the -override argument back (it is removed automatically be CheckArg) if there is one michael@0: if (override) { michael@0: gRestartArgv[gRestartArgc++] = const_cast("-override"); michael@0: gRestartArgv[gRestartArgc++] = const_cast(override); michael@0: } michael@0: michael@0: gRestartArgv[gRestartArgc] = nullptr; michael@0: michael@0: michael@0: if (EnvHasValue("MOZ_SAFE_MODE_RESTART")) { michael@0: gSafeMode = true; michael@0: // unset the env variable michael@0: SaveToEnv("MOZ_SAFE_MODE_RESTART="); michael@0: } michael@0: michael@0: ar = CheckArg("safe-mode", true); michael@0: if (ar == ARG_BAD) { michael@0: PR_fprintf(PR_STDERR, "Error: argument -safe-mode is invalid when argument -osint is specified\n"); michael@0: return 1; michael@0: } else if (ar == ARG_FOUND) { michael@0: gSafeMode = true; michael@0: } michael@0: michael@0: #ifdef XP_WIN michael@0: // If the shift key is pressed and the ctrl and / or alt keys are not pressed michael@0: // during startup start in safe mode. GetKeyState returns a short and the high michael@0: // order bit will be 1 if the key is pressed. By masking the returned short michael@0: // with 0x8000 the result will be 0 if the key is not pressed and non-zero michael@0: // otherwise. michael@0: if (GetKeyState(VK_SHIFT) & 0x8000 && michael@0: !(GetKeyState(VK_CONTROL) & 0x8000) && !(GetKeyState(VK_MENU) & 0x8000)) { michael@0: gSafeMode = true; michael@0: } michael@0: #endif michael@0: michael@0: #ifdef XP_MACOSX michael@0: if (GetCurrentEventKeyModifiers() & optionKey) michael@0: gSafeMode = true; michael@0: #endif michael@0: michael@0: // In the Tor Browser, remoting is disabled by default unless -osint is used. michael@0: bool allowRemote = (CheckArg("allow-remote") == ARG_FOUND); michael@0: if (!allowRemote && (CheckArg("osint", false, nullptr, false) != ARG_FOUND)) michael@0: SaveToEnv("MOZ_NO_REMOTE=1"); michael@0: michael@0: // Handle -no-remote and -new-instance command line arguments. Setup michael@0: // the environment to better accommodate other components and various michael@0: // restart scenarios. michael@0: ar = CheckArg("no-remote", true); michael@0: if (ar == ARG_BAD) { michael@0: PR_fprintf(PR_STDERR, "Error: argument -no-remote is invalid when argument -osint is specified\n"); michael@0: return 1; michael@0: } else if ((ar == ARG_FOUND) && allowRemote) { michael@0: PR_fprintf(PR_STDERR, "Error: argument -no-remote is invalid when argument -allow-remote is specified\n"); michael@0: return 1; michael@0: } michael@0: michael@0: ar = CheckArg("new-instance", true); michael@0: if (ar == ARG_BAD) { michael@0: PR_fprintf(PR_STDERR, "Error: argument -new-instance is invalid when argument -osint is specified\n"); michael@0: return 1; michael@0: } else if (ar == ARG_FOUND) { michael@0: SaveToEnv("MOZ_NEW_INSTANCE=1"); michael@0: } michael@0: michael@0: // Handle -help and -version command line arguments. michael@0: // They should return quickly, so we deal with them here. michael@0: if (CheckArg("h") || CheckArg("help") || CheckArg("?")) { michael@0: DumpHelp(); michael@0: *aExitFlag = true; michael@0: return 0; michael@0: } michael@0: michael@0: if (CheckArg("v") || CheckArg("version")) { michael@0: DumpVersion(); michael@0: *aExitFlag = true; michael@0: return 0; michael@0: } michael@0: michael@0: #ifdef NS_TRACE_MALLOC michael@0: gArgc = NS_TraceMallocStartupArgs(gArgc, gArgv); michael@0: #endif michael@0: michael@0: rv = XRE_InitCommandLine(gArgc, gArgv); michael@0: NS_ENSURE_SUCCESS(rv, 1); michael@0: michael@0: // Check for -register, which registers chrome and then exits immediately. michael@0: ar = CheckArg("register", true); michael@0: if (ar == ARG_BAD) { michael@0: PR_fprintf(PR_STDERR, "Error: argument -register is invalid when argument -osint is specified\n"); michael@0: return 1; michael@0: } else if (ar == ARG_FOUND) { michael@0: ScopedXPCOMStartup xpcom; michael@0: rv = xpcom.Initialize(); michael@0: NS_ENSURE_SUCCESS(rv, 1); michael@0: { michael@0: nsCOMPtr chromeReg = michael@0: mozilla::services::GetChromeRegistryService(); michael@0: NS_ENSURE_TRUE(chromeReg, 1); michael@0: michael@0: chromeReg->CheckForNewChrome(); michael@0: } michael@0: *aExitFlag = true; michael@0: return 0; michael@0: } michael@0: michael@0: if (PR_GetEnv("MOZ_RUN_GTEST")) { michael@0: int result; michael@0: // RunGTest will only be set if we're in xul-unit michael@0: if (mozilla::RunGTest) { michael@0: result = mozilla::RunGTest(); michael@0: } else { michael@0: result = 1; michael@0: printf("TEST-UNEXPECTED-FAIL | gtest | Not compiled with enable-tests\n"); michael@0: } michael@0: *aExitFlag = true; michael@0: return result; michael@0: } michael@0: michael@0: return 0; michael@0: } michael@0: michael@0: #ifdef MOZ_CRASHREPORTER michael@0: #ifdef XP_WIN michael@0: /** michael@0: * Uses WMI to read some manufacturer information that may be useful for michael@0: * diagnosing hardware-specific crashes. This function is best-effort; failures michael@0: * shouldn't burden the caller. COM must be initialized before calling. michael@0: */ michael@0: static void AnnotateSystemManufacturer() michael@0: { michael@0: nsRefPtr locator; michael@0: michael@0: HRESULT hr = CoCreateInstance(CLSID_WbemLocator, nullptr, CLSCTX_INPROC_SERVER, michael@0: IID_IWbemLocator, getter_AddRefs(locator)); michael@0: michael@0: if (FAILED(hr)) { michael@0: return; michael@0: } michael@0: michael@0: nsRefPtr services; michael@0: michael@0: hr = locator->ConnectServer(_bstr_t(L"ROOT\\CIMV2"), nullptr, nullptr, nullptr, michael@0: 0, nullptr, nullptr, getter_AddRefs(services)); michael@0: michael@0: if (FAILED(hr)) { michael@0: return; michael@0: } michael@0: michael@0: hr = CoSetProxyBlanket(services, RPC_C_AUTHN_WINNT, RPC_C_AUTHZ_NONE, nullptr, michael@0: RPC_C_AUTHN_LEVEL_CALL, RPC_C_IMP_LEVEL_IMPERSONATE, michael@0: nullptr, EOAC_NONE); michael@0: michael@0: if (FAILED(hr)) { michael@0: return; michael@0: } michael@0: michael@0: nsRefPtr enumerator; michael@0: michael@0: hr = services->ExecQuery(_bstr_t(L"WQL"), _bstr_t(L"SELECT * FROM Win32_BIOS"), michael@0: WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY, michael@0: nullptr, getter_AddRefs(enumerator)); michael@0: michael@0: if (FAILED(hr) || !enumerator) { michael@0: return; michael@0: } michael@0: michael@0: nsRefPtr classObject; michael@0: ULONG results; michael@0: michael@0: hr = enumerator->Next(WBEM_INFINITE, 1, getter_AddRefs(classObject), &results); michael@0: michael@0: if (FAILED(hr) || results == 0) { michael@0: return; michael@0: } michael@0: michael@0: VARIANT value; michael@0: VariantInit(&value); michael@0: michael@0: hr = classObject->Get(L"Manufacturer", 0, &value, 0, 0); michael@0: michael@0: if (SUCCEEDED(hr) && V_VT(&value) == VT_BSTR) { michael@0: CrashReporter::AnnotateCrashReport(NS_LITERAL_CSTRING("BIOS_Manufacturer"), michael@0: NS_ConvertUTF16toUTF8(V_BSTR(&value))); michael@0: } michael@0: michael@0: VariantClear(&value); michael@0: } michael@0: michael@0: static void PR_CALLBACK AnnotateSystemManufacturer_ThreadStart(void*) michael@0: { michael@0: HRESULT hr = CoInitialize(nullptr); michael@0: michael@0: if (FAILED(hr)) { michael@0: return; michael@0: } michael@0: michael@0: AnnotateSystemManufacturer(); michael@0: michael@0: CoUninitialize(); michael@0: } michael@0: #endif michael@0: #endif michael@0: michael@0: namespace mozilla { michael@0: ShutdownChecksMode gShutdownChecks = SCM_NOTHING; michael@0: } michael@0: michael@0: static void SetShutdownChecks() { michael@0: // Set default first. On debug builds we crash. On nightly and local michael@0: // builds we record. Nightlies will then send the info via telemetry, michael@0: // but it is usefull to have the data in about:telemetry in local builds michael@0: // too. michael@0: michael@0: #ifdef DEBUG michael@0: gShutdownChecks = SCM_CRASH; michael@0: #else michael@0: const char* releaseChannel = NS_STRINGIFY(MOZ_UPDATE_CHANNEL); michael@0: if (strcmp(releaseChannel, "nightly") == 0 || michael@0: strcmp(releaseChannel, "default") == 0) { michael@0: gShutdownChecks = SCM_RECORD; michael@0: } else { michael@0: gShutdownChecks = SCM_NOTHING; michael@0: } michael@0: #endif michael@0: michael@0: // We let an environment variable override the default so that addons michael@0: // authors can use it for debugging shutdown with released firefox versions. michael@0: const char* mozShutdownChecksEnv = PR_GetEnv("MOZ_SHUTDOWN_CHECKS"); michael@0: if (mozShutdownChecksEnv) { michael@0: if (strcmp(mozShutdownChecksEnv, "crash") == 0) { michael@0: gShutdownChecks = SCM_CRASH; michael@0: } else if (strcmp(mozShutdownChecksEnv, "record") == 0) { michael@0: gShutdownChecks = SCM_RECORD; michael@0: } else if (strcmp(mozShutdownChecksEnv, "nothing") == 0) { michael@0: gShutdownChecks = SCM_NOTHING; michael@0: } michael@0: } michael@0: michael@0: } michael@0: michael@0: /* michael@0: * XRE_mainStartup - Initializes the profile and various other services. michael@0: * Main() will exit early if either return value != 0 or if aExitFlag is michael@0: * true. michael@0: */ michael@0: int michael@0: XREMain::XRE_mainStartup(bool* aExitFlag) michael@0: { michael@0: nsresult rv; michael@0: michael@0: if (!aExitFlag) michael@0: return 1; michael@0: *aExitFlag = false; michael@0: michael@0: SetShutdownChecks(); michael@0: michael@0: michael@0: // Enable Telemetry IO Reporting on DEBUG, nightly and local builds michael@0: #ifdef DEBUG michael@0: mozilla::Telemetry::InitIOReporting(gAppData->xreDirectory); michael@0: #else michael@0: { michael@0: const char* releaseChannel = NS_STRINGIFY(MOZ_UPDATE_CHANNEL); michael@0: if (strcmp(releaseChannel, "nightly") == 0 || michael@0: strcmp(releaseChannel, "default") == 0) { michael@0: mozilla::Telemetry::InitIOReporting(gAppData->xreDirectory); michael@0: } michael@0: } michael@0: #endif /* DEBUG */ michael@0: michael@0: #if defined(MOZ_WIDGET_GTK) || defined(MOZ_ENABLE_XREMOTE) michael@0: // Stash DESKTOP_STARTUP_ID in malloc'ed memory because gtk_init will clear it. michael@0: #define HAVE_DESKTOP_STARTUP_ID michael@0: const char* desktopStartupIDEnv = PR_GetEnv("DESKTOP_STARTUP_ID"); michael@0: if (desktopStartupIDEnv) { michael@0: mDesktopStartupID.Assign(desktopStartupIDEnv); michael@0: } michael@0: #endif michael@0: michael@0: #if defined(MOZ_WIDGET_QT) michael@0: nsQAppInstance::AddRef(gArgc, gArgv, true); michael@0: michael@0: QStringList nonQtArguments = qApp->arguments(); michael@0: gQtOnlyArgc = 1; michael@0: gQtOnlyArgv = (char**) malloc(sizeof(char*) michael@0: * (gRestartArgc - nonQtArguments.size() + 2)); michael@0: michael@0: // copy binary path michael@0: gQtOnlyArgv[0] = gRestartArgv[0]; michael@0: michael@0: for (int i = 1; i < gRestartArgc; ++i) { michael@0: if (!nonQtArguments.contains(gRestartArgv[i])) { michael@0: // copy arguments used by Qt for later michael@0: gQtOnlyArgv[gQtOnlyArgc++] = gRestartArgv[i]; michael@0: } michael@0: } michael@0: gQtOnlyArgv[gQtOnlyArgc] = nullptr; michael@0: #endif michael@0: #if defined(MOZ_WIDGET_GTK) michael@0: // setup for private colormap. Ideally we'd like to do this michael@0: // in nsAppShell::Create, but we need to get in before gtk michael@0: // has been initialized to make sure everything is running michael@0: // consistently. michael@0: #if (MOZ_WIDGET_GTK == 2) michael@0: if (CheckArg("install")) michael@0: gdk_rgb_set_install(TRUE); michael@0: #endif michael@0: michael@0: // Set program name to the one defined in application.ini. michael@0: { michael@0: nsAutoCString program(gAppData->name); michael@0: ToLowerCase(program); michael@0: g_set_prgname(program.get()); michael@0: } michael@0: michael@0: // Initialize GTK here for splash. michael@0: michael@0: // Open the display ourselves instead of using gtk_init, so that we can michael@0: // close it without fear that one day gtk might clean up the display it michael@0: // opens. michael@0: if (!gtk_parse_args(&gArgc, &gArgv)) michael@0: return 1; michael@0: michael@0: // display_name is owned by gdk. michael@0: const char *display_name = gdk_get_display_arg_name(); michael@0: if (display_name) { michael@0: SaveWordToEnv("DISPLAY", nsDependentCString(display_name)); michael@0: } else { michael@0: display_name = PR_GetEnv("DISPLAY"); michael@0: if (!display_name) { michael@0: PR_fprintf(PR_STDERR, "Error: no display specified\n"); michael@0: return 1; michael@0: } michael@0: } michael@0: #endif /* MOZ_WIDGET_GTK2 */ michael@0: michael@0: #ifdef MOZ_ENABLE_XREMOTE michael@0: // handle -remote now that xpcom is fired up michael@0: bool newInstance; michael@0: { michael@0: char *e = PR_GetEnv("MOZ_NO_REMOTE"); michael@0: mDisableRemote = (e && *e); michael@0: if (mDisableRemote) { michael@0: newInstance = true; michael@0: } else { michael@0: e = PR_GetEnv("MOZ_NEW_INSTANCE"); michael@0: newInstance = (e && *e); michael@0: } michael@0: } michael@0: michael@0: const char* xremotearg; michael@0: ArgResult ar = CheckArg("remote", true, &xremotearg); michael@0: if (ar == ARG_BAD) { michael@0: PR_fprintf(PR_STDERR, "Error: -remote requires an argument\n"); michael@0: return 1; michael@0: } michael@0: const char* desktopStartupIDPtr = michael@0: mDesktopStartupID.IsEmpty() ? nullptr : mDesktopStartupID.get(); michael@0: if (ar) { michael@0: *aExitFlag = true; michael@0: return HandleRemoteArgument(xremotearg, desktopStartupIDPtr); michael@0: } michael@0: michael@0: if (!newInstance) { michael@0: // Try to remote the entire command line. If this fails, start up normally. michael@0: RemoteResult rr = RemoteCommandLine(desktopStartupIDPtr); michael@0: if (rr == REMOTE_FOUND) { michael@0: *aExitFlag = true; michael@0: return 0; michael@0: } michael@0: else if (rr == REMOTE_ARG_BAD) michael@0: return 1; michael@0: } michael@0: #endif michael@0: #ifdef MOZ_X11 michael@0: // Init X11 in thread-safe mode. Must be called prior to the first call to XOpenDisplay michael@0: // (called inside gdk_display_open). This is a requirement for off main tread compositing. michael@0: // This is done only on X11 platforms if the environment variable MOZ_USE_OMTC is set so michael@0: // as to avoid overhead when omtc is not used. michael@0: // michael@0: // On nightly builds, we call this by default to enable OMTC for Electrolysis testing. On michael@0: // aurora, beta, and release builds, there is a small tpaint regression from enabling this michael@0: // call, so it sits behind an environment variable. michael@0: // michael@0: // An environment variable is used instead of a pref on X11 platforms because we start having michael@0: // access to prefs long after the first call to XOpenDisplay which is hard to change due to michael@0: // interdependencies in the initialization. michael@0: # ifndef NIGHTLY_BUILD michael@0: if (PR_GetEnv("MOZ_USE_OMTC") || michael@0: PR_GetEnv("MOZ_OMTC_ENABLED")) michael@0: # endif michael@0: { michael@0: XInitThreads(); michael@0: } michael@0: #endif michael@0: #if defined(MOZ_WIDGET_GTK) michael@0: mGdkDisplay = gdk_display_open(display_name); michael@0: if (!mGdkDisplay) { michael@0: PR_fprintf(PR_STDERR, "Error: cannot open display: %s\n", display_name); michael@0: return 1; michael@0: } michael@0: gdk_display_manager_set_default_display (gdk_display_manager_get(), michael@0: mGdkDisplay); michael@0: michael@0: // g_set_application_name () is only defined in glib2.2 and higher. michael@0: _g_set_application_name_fn _g_set_application_name = michael@0: (_g_set_application_name_fn)FindFunction("g_set_application_name"); michael@0: if (_g_set_application_name) { michael@0: _g_set_application_name(mAppData->name); michael@0: } michael@0: _gtk_window_set_auto_startup_notification_fn _gtk_window_set_auto_startup_notification = michael@0: (_gtk_window_set_auto_startup_notification_fn)FindFunction("gtk_window_set_auto_startup_notification"); michael@0: if (_gtk_window_set_auto_startup_notification) { michael@0: _gtk_window_set_auto_startup_notification(false); michael@0: } michael@0: michael@0: #if (MOZ_WIDGET_GTK == 2) michael@0: gtk_widget_set_default_colormap(gdk_rgb_get_colormap()); michael@0: #endif /* (MOZ_WIDGET_GTK == 2) */ michael@0: #endif /* defined(MOZ_WIDGET_GTK) */ michael@0: #ifdef MOZ_X11 michael@0: // Do this after initializing GDK, or GDK will install its own handler. michael@0: InstallX11ErrorHandler(); michael@0: #endif michael@0: michael@0: // Call the code to install our handler michael@0: #ifdef MOZ_JPROF michael@0: setupProfilingStuff(); michael@0: #endif michael@0: michael@0: rv = NS_CreateNativeAppSupport(getter_AddRefs(mNativeApp)); michael@0: if (NS_FAILED(rv)) michael@0: return 1; michael@0: michael@0: bool canRun = false; michael@0: rv = mNativeApp->Start(&canRun); michael@0: if (NS_FAILED(rv) || !canRun) { michael@0: return 1; michael@0: } michael@0: michael@0: #if defined(HAVE_DESKTOP_STARTUP_ID) && defined(MOZ_WIDGET_GTK) michael@0: // DESKTOP_STARTUP_ID is cleared now, michael@0: // we recover it in case we need a restart. michael@0: if (!mDesktopStartupID.IsEmpty()) { michael@0: nsAutoCString desktopStartupEnv; michael@0: desktopStartupEnv.AssignLiteral("DESKTOP_STARTUP_ID="); michael@0: desktopStartupEnv.Append(mDesktopStartupID); michael@0: // Leak it with extreme prejudice! michael@0: PR_SetEnv(ToNewCString(desktopStartupEnv)); michael@0: } michael@0: #endif michael@0: michael@0: #if defined(USE_MOZ_UPDATER) michael@0: // Check for and process any available updates michael@0: nsCOMPtr updRoot; michael@0: bool persistent; michael@0: rv = mDirProvider.GetFile(XRE_UPDATE_ROOT_DIR, &persistent, michael@0: getter_AddRefs(updRoot)); michael@0: // XRE_UPDATE_ROOT_DIR may fail. Fallback to appDir if failed michael@0: if (NS_FAILED(rv)) michael@0: updRoot = mDirProvider.GetAppDir(); michael@0: michael@0: // If the MOZ_PROCESS_UPDATES environment variable already exists, then michael@0: // we are being called from the callback application. michael@0: if (EnvHasValue("MOZ_PROCESS_UPDATES")) { michael@0: // If the caller has asked us to log our arguments, do so. This is used michael@0: // to make sure that the maintenance service successfully launches the michael@0: // callback application. michael@0: const char *logFile = nullptr; michael@0: if (ARG_FOUND == CheckArg("dump-args", false, &logFile)) { michael@0: FILE* logFP = fopen(logFile, "wb"); michael@0: if (logFP) { michael@0: for (int i = 1; i < gRestartArgc; ++i) { michael@0: fprintf(logFP, "%s\n", gRestartArgv[i]); michael@0: } michael@0: fclose(logFP); michael@0: } michael@0: } michael@0: *aExitFlag = true; michael@0: return 0; michael@0: } michael@0: michael@0: // Support for processing an update and exiting. The MOZ_PROCESS_UPDATES michael@0: // environment variable will be part of the updater's environment and the michael@0: // application that is relaunched by the updater. When the application is michael@0: // relaunched by the updater it will be removed below and the application michael@0: // will exit. michael@0: if (CheckArg("process-updates")) { michael@0: SaveToEnv("MOZ_PROCESS_UPDATES=1"); michael@0: } michael@0: nsCOMPtr exeFile, exeDir; michael@0: rv = mDirProvider.GetFile(XRE_EXECUTABLE_FILE, &persistent, michael@0: getter_AddRefs(exeFile)); michael@0: NS_ENSURE_SUCCESS(rv, 1); michael@0: rv = exeFile->GetParent(getter_AddRefs(exeDir)); michael@0: NS_ENSURE_SUCCESS(rv, 1); michael@0: #ifdef TOR_BROWSER_UPDATE michael@0: nsAutoCString compatVersion(TOR_BROWSER_VERSION); michael@0: #endif michael@0: ProcessUpdates(mDirProvider.GetGREDir(), michael@0: exeDir, michael@0: updRoot, michael@0: gRestartArgc, michael@0: gRestartArgv, michael@0: #ifdef TOR_BROWSER_UPDATE michael@0: compatVersion.get() michael@0: #else michael@0: mAppData->version michael@0: #endif michael@0: ); michael@0: if (EnvHasValue("MOZ_PROCESS_UPDATES")) { michael@0: SaveToEnv("MOZ_PROCESS_UPDATES="); michael@0: *aExitFlag = true; michael@0: return 0; michael@0: } michael@0: #if defined(XP_WIN) && defined(MOZ_METRO) michael@0: if (CheckArg("metro-update", false) == ARG_FOUND) { michael@0: *aExitFlag = true; michael@0: return 0; michael@0: } michael@0: #endif michael@0: #endif michael@0: michael@0: rv = NS_NewToolkitProfileService(getter_AddRefs(mProfileSvc)); michael@0: if (rv == NS_ERROR_FILE_ACCESS_DENIED) { michael@0: PR_fprintf(PR_STDERR, "Error: Access was denied while trying to open files in " \ michael@0: "your profile directory.\n"); michael@0: } michael@0: if (NS_FAILED(rv)) { michael@0: // We failed to choose or create profile - notify user and quit michael@0: ProfileMissingDialog(mNativeApp); michael@0: return 1; michael@0: } michael@0: michael@0: rv = SelectProfile(getter_AddRefs(mProfileLock), mProfileSvc, mNativeApp, &mStartOffline, michael@0: &mProfileName); michael@0: if (rv == NS_ERROR_LAUNCHED_CHILD_PROCESS || michael@0: rv == NS_ERROR_ABORT) { michael@0: *aExitFlag = true; michael@0: return 0; michael@0: } michael@0: michael@0: if (NS_FAILED(rv)) { michael@0: // We failed to choose or create profile - notify user and quit michael@0: ProfileMissingDialog(mNativeApp); michael@0: return 1; michael@0: } michael@0: gProfileLock = mProfileLock; michael@0: michael@0: rv = mProfileLock->GetDirectory(getter_AddRefs(mProfD)); michael@0: NS_ENSURE_SUCCESS(rv, 1); michael@0: michael@0: rv = mProfileLock->GetLocalDirectory(getter_AddRefs(mProfLD)); michael@0: NS_ENSURE_SUCCESS(rv, 1); michael@0: michael@0: rv = mDirProvider.SetProfile(mProfD, mProfLD); michael@0: NS_ENSURE_SUCCESS(rv, 1); michael@0: michael@0: //////////////////////// NOW WE HAVE A PROFILE //////////////////////// michael@0: michael@0: mozilla::Telemetry::SetProfileDir(mProfD); michael@0: michael@0: #ifdef MOZ_CRASHREPORTER michael@0: if (mAppData->flags & NS_XRE_ENABLE_CRASH_REPORTER) michael@0: MakeOrSetMinidumpPath(mProfD); michael@0: michael@0: CrashReporter::UpdateCrashEventsDir(); michael@0: #endif michael@0: michael@0: nsAutoCString version; michael@0: BuildVersion(version); michael@0: michael@0: #ifdef TARGET_OS_ABI michael@0: NS_NAMED_LITERAL_CSTRING(osABI, TARGET_OS_ABI); michael@0: #else michael@0: // No TARGET_XPCOM_ABI, but at least the OS is known michael@0: NS_NAMED_LITERAL_CSTRING(osABI, OS_TARGET "_UNKNOWN"); michael@0: #endif michael@0: michael@0: // Check for version compatibility with the last version of the app this michael@0: // profile was started with. The format of the version stamp is defined michael@0: // by the BuildVersion function. michael@0: // Also check to see if something has happened to invalidate our michael@0: // fastload caches, like an extension upgrade or installation. michael@0: michael@0: // If we see .purgecaches, that means someone did a make. michael@0: // Re-register components to catch potential changes. michael@0: nsCOMPtr flagFile; michael@0: michael@0: rv = NS_ERROR_FILE_NOT_FOUND; michael@0: nsCOMPtr fFlagFile; michael@0: if (mAppData->directory) { michael@0: rv = mAppData->directory->Clone(getter_AddRefs(fFlagFile)); michael@0: } michael@0: flagFile = do_QueryInterface(fFlagFile); michael@0: if (flagFile) { michael@0: flagFile->AppendNative(FILE_INVALIDATE_CACHES); michael@0: } michael@0: michael@0: bool cachesOK; michael@0: bool versionOK = CheckCompatibility(mProfD, version, osABI, michael@0: mDirProvider.GetGREDir(), michael@0: mAppData->directory, flagFile, michael@0: &cachesOK); michael@0: if (CheckArg("purgecaches")) { michael@0: cachesOK = false; michael@0: } michael@0: if (PR_GetEnv("MOZ_PURGE_CACHES")) { michael@0: cachesOK = false; michael@0: } michael@0: michael@0: // Every time a profile is loaded by a build with a different version, michael@0: // it updates the compatibility.ini file saying what version last wrote michael@0: // the fastload caches. On subsequent launches if the version matches, michael@0: // there is no need for re-registration. If the user loads the same michael@0: // profile in different builds the component registry must be michael@0: // re-generated to prevent mysterious component loading failures. michael@0: // michael@0: bool startupCacheValid = true; michael@0: if (gSafeMode) { michael@0: startupCacheValid = RemoveComponentRegistries(mProfD, mProfLD, false); michael@0: WriteVersion(mProfD, NS_LITERAL_CSTRING("Safe Mode"), osABI, michael@0: mDirProvider.GetGREDir(), mAppData->directory, !startupCacheValid); michael@0: } michael@0: else if (versionOK) { michael@0: if (!cachesOK) { michael@0: // Remove caches, forcing component re-registration. michael@0: // The new list of additional components directories is derived from michael@0: // information in "extensions.ini". michael@0: startupCacheValid = RemoveComponentRegistries(mProfD, mProfLD, false); michael@0: michael@0: // Rewrite compatibility.ini to remove the flag michael@0: WriteVersion(mProfD, version, osABI, michael@0: mDirProvider.GetGREDir(), mAppData->directory, !startupCacheValid); michael@0: } michael@0: // Nothing need be done for the normal startup case. michael@0: } michael@0: else { michael@0: // Remove caches, forcing component re-registration michael@0: // with the default set of components (this disables any potentially michael@0: // troublesome incompatible XPCOM components). michael@0: startupCacheValid = RemoveComponentRegistries(mProfD, mProfLD, true); michael@0: michael@0: // Write out version michael@0: WriteVersion(mProfD, version, osABI, michael@0: mDirProvider.GetGREDir(), mAppData->directory, !startupCacheValid); michael@0: } michael@0: michael@0: if (!startupCacheValid) michael@0: StartupCache::IgnoreDiskCache(); michael@0: michael@0: if (flagFile) { michael@0: flagFile->Remove(true); michael@0: } michael@0: michael@0: return 0; michael@0: } michael@0: michael@0: /* michael@0: * XRE_mainRun - Command line startup, profile migration, and michael@0: * the calling of appStartup->Run(). michael@0: */ michael@0: nsresult michael@0: XREMain::XRE_mainRun() michael@0: { michael@0: nsresult rv = NS_OK; michael@0: NS_ASSERTION(mScopedXPCom, "Scoped xpcom not initialized."); michael@0: michael@0: #ifdef NS_FUNCTION_TIMER michael@0: // initialize some common services, so we don't pay the cost for these at odd times later on; michael@0: // SetWindowCreator -> ChromeRegistry -> IOService -> SocketTransportService -> (nspr wspm init), Prefs michael@0: { michael@0: nsCOMPtr comp; michael@0: michael@0: comp = do_GetService("@mozilla.org/preferences-service;1"); michael@0: michael@0: comp = do_GetService("@mozilla.org/network/socket-transport-service;1"); michael@0: michael@0: comp = do_GetService("@mozilla.org/network/dns-service;1"); michael@0: michael@0: comp = do_GetService("@mozilla.org/network/io-service;1"); michael@0: michael@0: comp = do_GetService("@mozilla.org/chrome/chrome-registry;1"); michael@0: michael@0: comp = do_GetService("@mozilla.org/focus-event-suppressor-service;1"); michael@0: } michael@0: #endif michael@0: michael@0: rv = mScopedXPCom->SetWindowCreator(mNativeApp); michael@0: NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE); michael@0: michael@0: #ifdef MOZ_CRASHREPORTER michael@0: // tell the crash reporter to also send the release channel michael@0: nsCOMPtr prefs = do_GetService("@mozilla.org/preferences-service;1", &rv); michael@0: if (NS_SUCCEEDED(rv)) { michael@0: nsCOMPtr defaultPrefBranch; michael@0: rv = prefs->GetDefaultBranch(nullptr, getter_AddRefs(defaultPrefBranch)); michael@0: michael@0: if (NS_SUCCEEDED(rv)) { michael@0: nsXPIDLCString sval; michael@0: rv = defaultPrefBranch->GetCharPref("app.update.channel", getter_Copies(sval)); michael@0: if (NS_SUCCEEDED(rv)) { michael@0: CrashReporter::AnnotateCrashReport(NS_LITERAL_CSTRING("ReleaseChannel"), michael@0: sval); michael@0: } michael@0: } michael@0: } michael@0: // Needs to be set after xpcom initialization. michael@0: CrashReporter::AnnotateCrashReport(NS_LITERAL_CSTRING("FramePoisonBase"), michael@0: nsPrintfCString("%.16llx", uint64_t(gMozillaPoisonBase))); michael@0: CrashReporter::AnnotateCrashReport(NS_LITERAL_CSTRING("FramePoisonSize"), michael@0: nsPrintfCString("%lu", uint32_t(gMozillaPoisonSize))); michael@0: michael@0: #ifdef XP_WIN michael@0: PR_CreateThread(PR_USER_THREAD, AnnotateSystemManufacturer_ThreadStart, 0, michael@0: PR_PRIORITY_LOW, PR_GLOBAL_THREAD, PR_UNJOINABLE_THREAD, 0); michael@0: #endif michael@0: michael@0: #endif michael@0: michael@0: if (mStartOffline) { michael@0: nsCOMPtr io (do_GetService("@mozilla.org/network/io-service;1")); michael@0: NS_ENSURE_TRUE(io, NS_ERROR_FAILURE); michael@0: io->SetManageOfflineStatus(false); michael@0: io->SetOffline(true); michael@0: } michael@0: michael@0: { michael@0: nsCOMPtr startupNotifier michael@0: (do_CreateInstance(NS_APPSTARTUPNOTIFIER_CONTRACTID, &rv)); michael@0: NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE); michael@0: michael@0: startupNotifier->Observe(nullptr, APPSTARTUP_TOPIC, nullptr); michael@0: } michael@0: michael@0: nsCOMPtr appStartup michael@0: (do_GetService(NS_APPSTARTUP_CONTRACTID)); michael@0: NS_ENSURE_TRUE(appStartup, NS_ERROR_FAILURE); michael@0: michael@0: if (gDoMigration) { michael@0: nsCOMPtr file; michael@0: mDirProvider.GetAppDir()->Clone(getter_AddRefs(file)); michael@0: file->AppendNative(NS_LITERAL_CSTRING("override.ini")); michael@0: nsINIParser parser; michael@0: nsresult rv = parser.Init(file); michael@0: if (NS_SUCCEEDED(rv)) { michael@0: nsAutoCString buf; michael@0: rv = parser.GetString("XRE", "EnableProfileMigrator", buf); michael@0: if (NS_SUCCEEDED(rv)) { michael@0: if (buf[0] == '0' || buf[0] == 'f' || buf[0] == 'F') { michael@0: gDoMigration = false; michael@0: } michael@0: } michael@0: } michael@0: } michael@0: michael@0: { michael@0: nsCOMPtr selectedProfile; michael@0: if (gDoProfileReset) { michael@0: // At this point we can be sure that profile reset is happening on the default profile. michael@0: rv = mProfileSvc->GetSelectedProfile(getter_AddRefs(selectedProfile)); michael@0: if (NS_FAILED(rv)) { michael@0: gDoProfileReset = false; michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: } michael@0: michael@0: // Profile Migration michael@0: if (mAppData->flags & NS_XRE_ENABLE_PROFILE_MIGRATOR && gDoMigration) { michael@0: gDoMigration = false; michael@0: nsCOMPtr pm(do_CreateInstance(NS_PROFILEMIGRATOR_CONTRACTID)); michael@0: if (pm) { michael@0: nsAutoCString aKey; michael@0: if (gDoProfileReset) { michael@0: // Automatically migrate from the current application if we just michael@0: // reset the profile. michael@0: aKey = MOZ_APP_NAME; michael@0: } michael@0: pm->Migrate(&mDirProvider, aKey); michael@0: } michael@0: } michael@0: michael@0: if (gDoProfileReset) { michael@0: nsresult backupCreated = ProfileResetCleanup(selectedProfile); michael@0: if (NS_FAILED(backupCreated)) NS_WARNING("Could not cleanup the profile that was reset"); michael@0: michael@0: // Set the new profile as the default after we're done cleaning up the old default. michael@0: rv = SetCurrentProfileAsDefault(mProfileSvc, mProfD); michael@0: if (NS_FAILED(rv)) NS_WARNING("Could not set current profile as the default"); michael@0: } michael@0: } michael@0: michael@0: mDirProvider.DoStartup(); michael@0: michael@0: #ifdef MOZ_CRASHREPORTER michael@0: nsCString userAgentLocale; michael@0: if (NS_SUCCEEDED(Preferences::GetCString("general.useragent.locale", &userAgentLocale))) { michael@0: CrashReporter::AnnotateCrashReport(NS_LITERAL_CSTRING("useragent_locale"), userAgentLocale); michael@0: } michael@0: #endif michael@0: michael@0: appStartup->GetShuttingDown(&mShuttingDown); michael@0: michael@0: nsCOMPtr cmdLine; michael@0: michael@0: nsCOMPtr workingDir; michael@0: rv = NS_GetSpecialDirectory(NS_OS_CURRENT_WORKING_DIR, getter_AddRefs(workingDir)); michael@0: NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE); michael@0: michael@0: if (!mShuttingDown) { michael@0: cmdLine = do_CreateInstance("@mozilla.org/toolkit/command-line;1"); michael@0: NS_ENSURE_TRUE(cmdLine, NS_ERROR_FAILURE); michael@0: michael@0: rv = cmdLine->Init(gArgc, gArgv, workingDir, michael@0: nsICommandLine::STATE_INITIAL_LAUNCH); michael@0: NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE); michael@0: michael@0: /* Special-case services that need early access to the command michael@0: line. */ michael@0: nsCOMPtr obsService = michael@0: mozilla::services::GetObserverService(); michael@0: if (obsService) { michael@0: obsService->NotifyObservers(cmdLine, "command-line-startup", nullptr); michael@0: } michael@0: } michael@0: michael@0: SaveStateForAppInitiatedRestart(); michael@0: michael@0: // clear out any environment variables which may have been set michael@0: // during the relaunch process now that we know we won't be relaunching. michael@0: SaveToEnv("XRE_PROFILE_PATH="); michael@0: SaveToEnv("XRE_PROFILE_LOCAL_PATH="); michael@0: SaveToEnv("XRE_PROFILE_NAME="); michael@0: SaveToEnv("XRE_START_OFFLINE="); michael@0: SaveToEnv("NO_EM_RESTART="); michael@0: SaveToEnv("XUL_APP_FILE="); michael@0: SaveToEnv("XRE_BINARY_PATH="); michael@0: michael@0: if (!mShuttingDown) { michael@0: rv = appStartup->CreateHiddenWindow(); michael@0: NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE); michael@0: michael@0: #if defined(HAVE_DESKTOP_STARTUP_ID) && defined(MOZ_WIDGET_GTK) michael@0: nsGTKToolkit* toolkit = nsGTKToolkit::GetToolkit(); michael@0: if (toolkit && !mDesktopStartupID.IsEmpty()) { michael@0: toolkit->SetDesktopStartupID(mDesktopStartupID); michael@0: } michael@0: // Clear the environment variable so it won't be inherited by michael@0: // child processes and confuse things. michael@0: g_unsetenv ("DESKTOP_STARTUP_ID"); michael@0: #endif michael@0: michael@0: #ifdef XP_MACOSX michael@0: // Set up ability to respond to system (Apple) events. This must be michael@0: // done before setting up the command line service. michael@0: SetupMacApplicationDelegate(); michael@0: michael@0: // we re-initialize the command-line service and do appleevents munging michael@0: // after we are sure that we're not restarting michael@0: cmdLine = do_CreateInstance("@mozilla.org/toolkit/command-line;1"); michael@0: NS_ENSURE_TRUE(cmdLine, NS_ERROR_FAILURE); michael@0: michael@0: CommandLineServiceMac::SetupMacCommandLine(gArgc, gArgv, false); michael@0: michael@0: rv = cmdLine->Init(gArgc, gArgv, michael@0: workingDir, nsICommandLine::STATE_INITIAL_LAUNCH); michael@0: NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE); michael@0: #endif michael@0: michael@0: nsCOMPtr obsService = michael@0: mozilla::services::GetObserverService(); michael@0: if (obsService) michael@0: obsService->NotifyObservers(nullptr, "final-ui-startup", nullptr); michael@0: michael@0: (void)appStartup->DoneStartingUp(); michael@0: appStartup->GetShuttingDown(&mShuttingDown); michael@0: } michael@0: michael@0: if (!mShuttingDown) { michael@0: rv = cmdLine->Run(); michael@0: NS_ENSURE_SUCCESS_LOG(rv, NS_ERROR_FAILURE); michael@0: michael@0: appStartup->GetShuttingDown(&mShuttingDown); michael@0: } michael@0: michael@0: if (!mShuttingDown) { michael@0: #ifdef MOZ_ENABLE_XREMOTE michael@0: // if we have X remote support, start listening for requests on the michael@0: // proxy window. michael@0: if (!mDisableRemote) michael@0: mRemoteService = do_GetService("@mozilla.org/toolkit/remote-service;1"); michael@0: if (mRemoteService) michael@0: mRemoteService->Startup(mAppData->name, mProfileName.get()); michael@0: #endif /* MOZ_ENABLE_XREMOTE */ michael@0: michael@0: mNativeApp->Enable(); michael@0: } michael@0: michael@0: #ifdef MOZ_INSTRUMENT_EVENT_LOOP michael@0: if (PR_GetEnv("MOZ_INSTRUMENT_EVENT_LOOP") || profiler_is_active()) { michael@0: bool logToConsole = !!PR_GetEnv("MOZ_INSTRUMENT_EVENT_LOOP"); michael@0: mozilla::InitEventTracing(logToConsole); michael@0: } michael@0: #endif /* MOZ_INSTRUMENT_EVENT_LOOP */ michael@0: michael@0: { michael@0: rv = appStartup->Run(); michael@0: if (NS_FAILED(rv)) { michael@0: NS_ERROR("failed to run appstartup"); michael@0: gLogConsoleErrors = true; michael@0: } michael@0: } michael@0: michael@0: return rv; michael@0: } michael@0: michael@0: /* michael@0: * XRE_main - A class based main entry point used by most platforms. michael@0: */ michael@0: int michael@0: XREMain::XRE_main(int argc, char* argv[], const nsXREAppData* aAppData) michael@0: { michael@0: char aLocal; michael@0: GeckoProfilerInitRAII profilerGuard(&aLocal); michael@0: PROFILER_LABEL("Startup", "XRE_Main"); michael@0: michael@0: mozilla::IOInterposerInit ioInterposerGuard; michael@0: michael@0: nsresult rv = NS_OK; michael@0: michael@0: gArgc = argc; michael@0: gArgv = argv; michael@0: michael@0: NS_ENSURE_TRUE(aAppData, 2); michael@0: michael@0: mAppData = new ScopedAppData(aAppData); michael@0: if (!mAppData) michael@0: return 1; michael@0: // used throughout this file michael@0: gAppData = mAppData; michael@0: michael@0: ScopedLogging log; michael@0: michael@0: #if defined(MOZ_WIDGET_GTK) michael@0: #if defined(MOZ_MEMORY) || defined(__FreeBSD__) || defined(__NetBSD__) michael@0: // Disable the slice allocator, since jemalloc already uses similar layout michael@0: // algorithms, and using a sub-allocator tends to increase fragmentation. michael@0: // This must be done before g_thread_init() is called. michael@0: g_slice_set_config(G_SLICE_CONFIG_ALWAYS_MALLOC, 1); michael@0: #endif michael@0: g_thread_init(nullptr); michael@0: #endif michael@0: michael@0: // init michael@0: bool exit = false; michael@0: int result = XRE_mainInit(&exit); michael@0: if (result != 0 || exit) michael@0: return result; michael@0: michael@0: // startup michael@0: result = XRE_mainStartup(&exit); michael@0: if (result != 0 || exit) michael@0: return result; michael@0: michael@0: bool appInitiatedRestart = false; michael@0: michael@0: // Start the real application michael@0: mScopedXPCom = new ScopedXPCOMStartup(); michael@0: if (!mScopedXPCom) michael@0: return 1; michael@0: michael@0: rv = mScopedXPCom->Initialize(); michael@0: NS_ENSURE_SUCCESS(rv, 1); michael@0: michael@0: // run! michael@0: rv = XRE_mainRun(); michael@0: michael@0: #ifdef MOZ_INSTRUMENT_EVENT_LOOP michael@0: mozilla::ShutdownEventTracing(); michael@0: #endif michael@0: michael@0: // Check for an application initiated restart. This is one that michael@0: // corresponds to nsIAppStartup.quit(eRestart) michael@0: if (rv == NS_SUCCESS_RESTART_APP || rv == NS_SUCCESS_RESTART_METRO_APP) { michael@0: appInitiatedRestart = true; michael@0: michael@0: // We have an application restart don't do any shutdown checks here michael@0: // In particular we don't want to poison IO for checking late-writes. michael@0: gShutdownChecks = SCM_NOTHING; michael@0: } michael@0: michael@0: if (!mShuttingDown) { michael@0: #ifdef MOZ_ENABLE_XREMOTE michael@0: // shut down the x remote proxy window michael@0: if (mRemoteService) { michael@0: mRemoteService->Shutdown(); michael@0: } michael@0: #endif /* MOZ_ENABLE_XREMOTE */ michael@0: } michael@0: michael@0: delete mScopedXPCom; michael@0: mScopedXPCom = nullptr; michael@0: michael@0: // unlock the profile after ScopedXPCOMStartup object (xpcom) michael@0: // has gone out of scope. see bug #386739 for more details michael@0: mProfileLock->Unlock(); michael@0: gProfileLock = nullptr; michael@0: michael@0: #if defined(MOZ_WIDGET_QT) michael@0: nsQAppInstance::Release(); michael@0: #endif michael@0: michael@0: // Restart the app after XPCOM has been shut down cleanly. michael@0: if (appInitiatedRestart) { michael@0: RestoreStateForAppInitiatedRestart(); michael@0: michael@0: // Ensure that these environment variables are set: michael@0: SaveFileToEnvIfUnset("XRE_PROFILE_PATH", mProfD); michael@0: SaveFileToEnvIfUnset("XRE_PROFILE_LOCAL_PATH", mProfLD); michael@0: SaveWordToEnvIfUnset("XRE_PROFILE_NAME", mProfileName); michael@0: michael@0: #ifdef MOZ_WIDGET_GTK michael@0: MOZ_gdk_display_close(mGdkDisplay); michael@0: #endif michael@0: michael@0: #if defined(MOZ_METRO) && defined(XP_WIN) michael@0: if (rv == NS_SUCCESS_RESTART_METRO_APP) { michael@0: LaunchDefaultMetroBrowser(); michael@0: rv = NS_OK; michael@0: } else michael@0: #endif michael@0: { michael@0: rv = LaunchChild(mNativeApp, true); michael@0: } michael@0: michael@0: #ifdef MOZ_CRASHREPORTER michael@0: if (mAppData->flags & NS_XRE_ENABLE_CRASH_REPORTER) michael@0: CrashReporter::UnsetExceptionHandler(); michael@0: #endif michael@0: return rv == NS_ERROR_LAUNCHED_CHILD_PROCESS ? 0 : 1; michael@0: } michael@0: michael@0: #ifdef MOZ_WIDGET_GTK michael@0: // gdk_display_close also calls gdk_display_manager_set_default_display michael@0: // appropriately when necessary. michael@0: MOZ_gdk_display_close(mGdkDisplay); michael@0: #endif michael@0: michael@0: #ifdef MOZ_CRASHREPORTER michael@0: if (mAppData->flags & NS_XRE_ENABLE_CRASH_REPORTER) michael@0: CrashReporter::UnsetExceptionHandler(); michael@0: #endif michael@0: michael@0: XRE_DeinitCommandLine(); michael@0: michael@0: return NS_FAILED(rv) ? 1 : 0; michael@0: } michael@0: michael@0: #if defined(MOZ_METRO) && defined(XP_WIN) michael@0: extern bool XRE_MetroCoreApplicationRun(); michael@0: static XREMain* xreMainPtr; michael@0: michael@0: // must be called by the thread we want as the main thread michael@0: nsresult michael@0: XRE_metroStartup(bool runXREMain) michael@0: { michael@0: nsresult rv; michael@0: michael@0: bool exit = false; michael@0: if (xreMainPtr->XRE_mainStartup(&exit) != 0 || exit) michael@0: return NS_ERROR_FAILURE; michael@0: michael@0: // Start the real application michael@0: xreMainPtr->mScopedXPCom = new ScopedXPCOMStartup(); michael@0: if (!xreMainPtr->mScopedXPCom) michael@0: return NS_ERROR_FAILURE; michael@0: michael@0: rv = xreMainPtr->mScopedXPCom->Initialize(); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: if (runXREMain) { michael@0: rv = xreMainPtr->XRE_mainRun(); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: } michael@0: return NS_OK; michael@0: } michael@0: michael@0: void michael@0: XRE_metroShutdown() michael@0: { michael@0: delete xreMainPtr->mScopedXPCom; michael@0: xreMainPtr->mScopedXPCom = nullptr; michael@0: michael@0: #ifdef MOZ_INSTRUMENT_EVENT_LOOP michael@0: mozilla::ShutdownEventTracing(); michael@0: #endif michael@0: michael@0: // unlock the profile after ScopedXPCOMStartup object (xpcom) michael@0: // has gone out of scope. see bug #386739 for more details michael@0: xreMainPtr->mProfileLock->Unlock(); michael@0: gProfileLock = nullptr; michael@0: michael@0: #ifdef MOZ_CRASHREPORTER michael@0: if (xreMainPtr->mAppData->flags & NS_XRE_ENABLE_CRASH_REPORTER) michael@0: CrashReporter::UnsetExceptionHandler(); michael@0: #endif michael@0: michael@0: XRE_DeinitCommandLine(); michael@0: } michael@0: michael@0: class WinRTInitWrapper michael@0: { michael@0: public: michael@0: WinRTInitWrapper() { michael@0: mResult = ::RoInitialize(RO_INIT_MULTITHREADED); michael@0: } michael@0: ~WinRTInitWrapper() { michael@0: if (SUCCEEDED(mResult)) { michael@0: ::RoUninitialize(); michael@0: } michael@0: } michael@0: HRESULT mResult; michael@0: }; michael@0: michael@0: int michael@0: XRE_mainMetro(int argc, char* argv[], const nsXREAppData* aAppData) michael@0: { michael@0: char aLocal; michael@0: GeckoProfilerInitRAII profilerGuard(&aLocal); michael@0: PROFILER_LABEL("Startup", "XRE_Main"); michael@0: michael@0: mozilla::IOInterposerInit ioInterposerGuard; michael@0: michael@0: nsresult rv = NS_OK; michael@0: michael@0: xreMainPtr = new XREMain(); michael@0: if (!xreMainPtr) { michael@0: return 1; michael@0: } michael@0: michael@0: // Inits Winrt and COM underneath it. michael@0: WinRTInitWrapper wrap; michael@0: michael@0: gArgc = argc; michael@0: gArgv = argv; michael@0: michael@0: NS_ENSURE_TRUE(aAppData, 2); michael@0: michael@0: xreMainPtr->mAppData = new ScopedAppData(aAppData); michael@0: if (!xreMainPtr->mAppData) michael@0: return 1; michael@0: // used throughout this file michael@0: gAppData = xreMainPtr->mAppData; michael@0: michael@0: ScopedLogging log; michael@0: michael@0: // init michael@0: bool exit = false; michael@0: if (xreMainPtr->XRE_mainInit(&exit) != 0 || exit) michael@0: return 1; michael@0: michael@0: // Located in widget, will call back into XRE_metroStartup and michael@0: // XRE_metroShutdown above. michael@0: if (!XRE_MetroCoreApplicationRun()) { michael@0: return 1; michael@0: } michael@0: michael@0: // XRE_metroShutdown should have already been called on the worker michael@0: // thread that called XRE_metroStartup. michael@0: NS_ASSERTION(!xreMainPtr->mScopedXPCom, michael@0: "XPCOM Shutdown hasn't occured, and we are exiting."); michael@0: return 0; michael@0: } michael@0: michael@0: void SetWindowsEnvironment(WindowsEnvironmentType aEnvID); michael@0: #endif // MOZ_METRO || !defined(XP_WIN) michael@0: michael@0: void michael@0: XRE_StopLateWriteChecks(void) { michael@0: mozilla::StopLateWriteChecks(); michael@0: } michael@0: michael@0: int michael@0: XRE_main(int argc, char* argv[], const nsXREAppData* aAppData, uint32_t aFlags) michael@0: { michael@0: #if !defined(MOZ_METRO) || !defined(XP_WIN) michael@0: XREMain main; michael@0: int result = main.XRE_main(argc, argv, aAppData); michael@0: mozilla::RecordShutdownEndTimeStamp(); michael@0: return result; michael@0: #else michael@0: if (aFlags == XRE_MAIN_FLAG_USE_METRO) { michael@0: SetWindowsEnvironment(WindowsEnvironmentType_Metro); michael@0: } michael@0: michael@0: // Desktop michael@0: if (XRE_GetWindowsEnvironment() == WindowsEnvironmentType_Desktop) { michael@0: XREMain main; michael@0: int result = main.XRE_main(argc, argv, aAppData); michael@0: mozilla::RecordShutdownEndTimeStamp(); michael@0: return result; michael@0: } michael@0: michael@0: // Metro michael@0: NS_ASSERTION(XRE_GetWindowsEnvironment() == WindowsEnvironmentType_Metro, michael@0: "Unknown Windows environment"); michael@0: michael@0: SetLastWinRunType(AHE_IMMERSIVE); michael@0: michael@0: int result = XRE_mainMetro(argc, argv, aAppData); michael@0: mozilla::RecordShutdownEndTimeStamp(); michael@0: return result; michael@0: #endif // MOZ_METRO || !defined(XP_WIN) michael@0: } michael@0: michael@0: nsresult michael@0: XRE_InitCommandLine(int aArgc, char* aArgv[]) michael@0: { michael@0: nsresult rv = NS_OK; michael@0: michael@0: #if defined(OS_WIN) michael@0: CommandLine::Init(aArgc, aArgv); michael@0: #else michael@0: michael@0: // these leak on error, but that's OK: we'll just exit() michael@0: char** canonArgs = new char*[aArgc]; michael@0: michael@0: // get the canonical version of the binary's path michael@0: nsCOMPtr binFile; michael@0: rv = XRE_GetBinaryPath(aArgv[0], getter_AddRefs(binFile)); michael@0: if (NS_FAILED(rv)) michael@0: return NS_ERROR_FAILURE; michael@0: michael@0: nsAutoCString canonBinPath; michael@0: rv = binFile->GetNativePath(canonBinPath); michael@0: if (NS_FAILED(rv)) michael@0: return NS_ERROR_FAILURE; michael@0: michael@0: canonArgs[0] = strdup(canonBinPath.get()); michael@0: michael@0: for (int i = 1; i < aArgc; ++i) { michael@0: if (aArgv[i]) { michael@0: canonArgs[i] = strdup(aArgv[i]); michael@0: } michael@0: } michael@0: michael@0: NS_ASSERTION(!CommandLine::IsInitialized(), "Bad news!"); michael@0: CommandLine::Init(aArgc, canonArgs); michael@0: michael@0: for (int i = 0; i < aArgc; ++i) michael@0: free(canonArgs[i]); michael@0: delete[] canonArgs; michael@0: #endif michael@0: michael@0: const char *path = nullptr; michael@0: ArgResult ar = CheckArg("greomni", false, &path); michael@0: if (ar == ARG_BAD) { michael@0: PR_fprintf(PR_STDERR, "Error: argument -greomni requires a path argument\n"); michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: if (!path) michael@0: return rv; michael@0: michael@0: nsCOMPtr greOmni; michael@0: rv = XRE_GetFileFromPath(path, getter_AddRefs(greOmni)); michael@0: if (NS_FAILED(rv)) { michael@0: PR_fprintf(PR_STDERR, "Error: argument -greomni requires a valid path\n"); michael@0: return rv; michael@0: } michael@0: michael@0: ar = CheckArg("appomni", false, &path); michael@0: if (ar == ARG_BAD) { michael@0: PR_fprintf(PR_STDERR, "Error: argument -appomni requires a path argument\n"); michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: nsCOMPtr appOmni; michael@0: if (path) { michael@0: rv = XRE_GetFileFromPath(path, getter_AddRefs(appOmni)); michael@0: if (NS_FAILED(rv)) { michael@0: PR_fprintf(PR_STDERR, "Error: argument -appomni requires a valid path\n"); michael@0: return rv; michael@0: } michael@0: } michael@0: michael@0: mozilla::Omnijar::Init(greOmni, appOmni); michael@0: return rv; michael@0: } michael@0: michael@0: nsresult michael@0: XRE_DeinitCommandLine() michael@0: { michael@0: nsresult rv = NS_OK; michael@0: michael@0: CommandLine::Terminate(); michael@0: michael@0: return rv; michael@0: } michael@0: michael@0: GeckoProcessType michael@0: XRE_GetProcessType() michael@0: { michael@0: return mozilla::startup::sChildProcessType; michael@0: } michael@0: michael@0: bool michael@0: mozilla::BrowserTabsRemote() michael@0: { michael@0: if (!gBrowserTabsRemoteInitialized) { michael@0: gBrowserTabsRemote = Preferences::GetBool("browser.tabs.remote", false); michael@0: gBrowserTabsRemoteInitialized = true; michael@0: } michael@0: michael@0: return gBrowserTabsRemote; michael@0: } michael@0: michael@0: void michael@0: SetupErrorHandling(const char* progname) michael@0: { michael@0: #ifdef XP_WIN michael@0: /* On Windows XPSP3 and Windows Vista if DEP is configured off-by-default michael@0: we still want DEP protection: enable it explicitly and programmatically. michael@0: michael@0: This function is not available on WinXPSP2 so we dynamically load it. michael@0: */ michael@0: michael@0: HMODULE kernel32 = GetModuleHandleW(L"kernel32.dll"); michael@0: SetProcessDEPPolicyFunc _SetProcessDEPPolicy = michael@0: (SetProcessDEPPolicyFunc) GetProcAddress(kernel32, "SetProcessDEPPolicy"); michael@0: if (_SetProcessDEPPolicy) michael@0: _SetProcessDEPPolicy(PROCESS_DEP_ENABLE); michael@0: #endif michael@0: michael@0: #ifdef XP_WIN32 michael@0: // Suppress the "DLL Foo could not be found" dialog, such that if dependent michael@0: // libraries (such as GDI+) are not preset, we gracefully fail to load those michael@0: // XPCOM components, instead of being ungraceful. michael@0: UINT realMode = SetErrorMode(0); michael@0: realMode |= SEM_FAILCRITICALERRORS; michael@0: // If XRE_NO_WINDOWS_CRASH_DIALOG is set, suppress displaying the "This michael@0: // application has crashed" dialog box. This is mainly useful for michael@0: // automated testing environments, e.g. tinderbox, where there's no need michael@0: // for a dozen of the dialog boxes to litter the console michael@0: if (getenv("XRE_NO_WINDOWS_CRASH_DIALOG")) michael@0: realMode |= SEM_NOGPFAULTERRORBOX | SEM_NOOPENFILEERRORBOX; michael@0: michael@0: SetErrorMode(realMode); michael@0: michael@0: #endif michael@0: michael@0: #if defined (DEBUG) && defined(XP_WIN) michael@0: // Send MSCRT Warnings, Errors and Assertions to stderr. michael@0: // See http://msdn.microsoft.com/en-us/library/1y71x448(v=VS.80).aspx michael@0: // and http://msdn.microsoft.com/en-us/library/a68f826y(v=VS.80).aspx. michael@0: michael@0: _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE); michael@0: _CrtSetReportFile(_CRT_WARN, _CRTDBG_FILE_STDERR); michael@0: _CrtSetReportMode(_CRT_ERROR, _CRTDBG_MODE_FILE); michael@0: _CrtSetReportFile(_CRT_ERROR, _CRTDBG_FILE_STDERR); michael@0: _CrtSetReportMode(_CRT_ASSERT, _CRTDBG_MODE_FILE); michael@0: _CrtSetReportFile(_CRT_ASSERT, _CRTDBG_FILE_STDERR); michael@0: michael@0: _CrtSetReportHook(MSCRTReportHook); michael@0: #endif michael@0: michael@0: InstallSignalHandlers(progname); michael@0: michael@0: // Unbuffer stdout, needed for tinderbox tests. michael@0: setbuf(stdout, 0); michael@0: } michael@0: