/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is Mozilla Communicator client code. * * The Initial Developer of the Original Code is * Netscape Communications Corporation. * Portions created by the Initial Developer are Copyright (C) 1998 * the Initial Developer. All Rights Reserved. * * Contributor(s): * Roland Mainz * Benjamin Smedberg * Ben Goodger * Fredrik Holmqvist * Ben Turner * Sergei Dolgov * * Alternatively, the contents of this file may be used under the terms of * either the GNU General Public License Version 2 or later (the "GPL"), or * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), * in which case the provisions of the GPL or the LGPL are applicable instead * of those above. If you wish to allow use of your version of this file only * under the terms of either the GPL or the LGPL, and not to allow others to * use your version of this file under the terms of the MPL, indicate your * decision by deleting the provisions above and replace them with the notice * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ #if defined(XP_OS2) && defined(MOZ_OS2_HIGH_MEMORY) // os2safe.h has to be included before os2.h, needed for high mem #include #endif #define XPCOM_TRANSLATE_NSGM_ENTRY_POINT 1 #if defined(MOZ_WIDGET_QT) #include #include #endif #include "nsAppRunner.h" #include "nsUpdateDriver.h" #ifdef XP_MACOSX #include "MacLaunchHelper.h" #include "MacApplicationDelegate.h" #include "MacAutoreleasePool.h" #endif #ifdef XP_OS2 #include "private/pprthred.h" #endif #include "prmem.h" #include "prnetdb.h" #include "prprf.h" #include "prproces.h" #include "prenv.h" #include "nsIAppShellService.h" #include "nsIAppStartup.h" #include "nsIAppStartupNotifier.h" #include "nsIMutableArray.h" #include "nsICategoryManager.h" #include "nsIChromeRegistry.h" #include "nsICommandLineRunner.h" #include "nsIComponentManager.h" #include "nsIComponentRegistrar.h" #include "nsIContentHandler.h" #include "nsIDialogParamBlock.h" #include "nsIDOMWindow.h" #include "nsIFastLoadService.h" // for PLATFORM_FASL_SUFFIX #include "nsIGenericFactory.h" #include "nsIIOService2.h" #include "nsIObserverService.h" #include "nsINativeAppSupport.h" #include "nsIProcess.h" #include "nsIProfileUnlocker.h" #include "nsIPromptService.h" #include "nsIServiceManager.h" #include "nsIStringBundle.h" #include "nsISupportsPrimitives.h" #include "nsITimelineService.h" #include "nsIToolkitChromeRegistry.h" #include "nsIToolkitProfile.h" #include "nsIToolkitProfileService.h" #include "nsIURI.h" #include "nsIWindowCreator.h" #include "nsIWindowMediator.h" #include "nsIWindowWatcher.h" #include "nsIXULAppInfo.h" #include "nsIXULRuntime.h" #include "nsPIDOMWindow.h" #include "nsIBaseWindow.h" #include "nsIWidget.h" #include "nsIDocShell.h" #include "nsAppShellCID.h" #include "mozilla/FunctionTimer.h" #ifdef XP_WIN #include "nsIWinAppHelper.h" #include #ifndef PROCESS_DEP_ENABLE #define PROCESS_DEP_ENABLE 0x1 #endif #endif #include "nsCRT.h" #include "nsCOMPtr.h" #include "nsDirectoryServiceDefs.h" #include "nsDirectoryServiceUtils.h" #include "nsEmbedCID.h" #include "nsNetUtil.h" #include "nsReadableUtils.h" #include "nsStaticComponents.h" #include "nsXPCOM.h" #include "nsXPCOMCIDInternal.h" #include "nsXPIDLString.h" #include "nsVersionComparator.h" #include "nsAppDirectoryServiceDefs.h" #include "nsXULAppAPI.h" #include "nsXREDirProvider.h" #include "nsToolkitCompsCID.h" #include "nsINIParser.h" #include "mozilla/Omnijar.h" #include #if defined(MOZ_SPLASHSCREEN) #include "nsSplashScreen.h" #endif #ifdef XP_UNIX #include #include #include #endif #ifdef XP_BEOS // execv() behaves bit differently in R5 and Zeta, looks unreliable in such situation //#include #include #include #endif //XP_BEOS #ifdef XP_WIN #ifndef WINCE #include #include #endif #include "nsThreadUtils.h" #endif #ifdef XP_MACOSX #include "nsILocalFileMac.h" #include "nsCommandLineServiceMac.h" #endif // for X remote support #ifdef MOZ_ENABLE_XREMOTE #include "XRemoteClient.h" #include "nsIRemoteService.h" #endif #ifdef NS_TRACE_MALLOC #include "nsTraceMalloc.h" #endif #if defined(DEBUG) && defined(XP_WIN32) #include #endif #if defined (XP_MACOSX) #include #endif #ifdef DEBUG #include "prlog.h" #endif #ifdef MOZ_JPROF #include "jprof.h" #endif #ifdef MOZ_CRASHREPORTER #include "nsExceptionHandler.h" #include "nsICrashReporter.h" #define NS_CRASHREPORTER_CONTRACTID "@mozilla.org/toolkit/crash-reporter;1" #include "nsIPrefService.h" #endif #ifdef MOZ_IPC #include "base/command_line.h" #endif #include "mozilla/FunctionTimer.h" #ifdef ANDROID #include "AndroidBridge.h" #endif #ifdef WINCE class WindowsMutex { public: WindowsMutex(const wchar_t *name) { mHandle = CreateMutexW(0, FALSE, name); } ~WindowsMutex() { Unlock(); CloseHandle(mHandle); } PRBool Lock(DWORD timeout = INFINITE) { DWORD state = WaitForSingleObject(mHandle, timeout); return state == WAIT_OBJECT_0; } void Unlock() { if (mHandle) ReleaseMutex(mHandle); } protected: HANDLE mHandle; }; #endif extern void InstallSignalHandlers(const char *ProgramName); #include "nsX11ErrorHandler.h" #define FILE_COMPATIBILITY_INFO NS_LITERAL_CSTRING("compatibility.ini") int gArgc; char **gArgv; static const char gToolkitVersion[] = NS_STRINGIFY(GRE_MILESTONE); static const char gToolkitBuildID[] = NS_STRINGIFY(GRE_BUILDID); static int gRestartArgc; static char **gRestartArgv; #if defined(MOZ_WIDGET_GTK2) #if defined(DEBUG) || defined(NS_BUILD_REFCNT_LOGGING) \ || defined(NS_TRACE_MALLOC) #define CLEANUP_MEMORY 1 #define PANGO_ENABLE_BACKEND #include #endif #include #ifdef MOZ_X11 #include #endif /* MOZ_X11 */ #include "nsGTKToolkit.h" #endif // Save literal putenv string to environment variable. static void SaveToEnv(const char *putenv) { char *expr = strdup(putenv); if (expr) PR_SetEnv(expr); // We intentionally leak |expr| here since it is required by PR_SetEnv. } // Save the given word to the specified environment variable. static void SaveWordToEnv(const char *name, const nsACString & word) { char *expr = PR_smprintf("%s=%s", name, PromiseFlatCString(word).get()); if (expr) PR_SetEnv(expr); // We intentionally leak |expr| here since it is required by PR_SetEnv. } // Save the path of the given file to the specified environment variable. static void SaveFileToEnv(const char *name, nsIFile *file) { #ifdef XP_WIN nsAutoString path; file->GetPath(path); SetEnvironmentVariableW(NS_ConvertASCIItoUTF16(name).get(), path.get()); #else nsCAutoString path; file->GetNativePath(path); SaveWordToEnv(name, path); #endif } // Load the path of a file saved with SaveFileToEnv static already_AddRefed GetFileFromEnv(const char *name) { nsresult rv; nsILocalFile *file = nsnull; #ifdef XP_WIN WCHAR path[_MAX_PATH]; if (!GetEnvironmentVariableW(NS_ConvertASCIItoUTF16(name).get(), path, _MAX_PATH)) return nsnull; rv = NS_NewLocalFile(nsDependentString(path), PR_TRUE, &file); if (NS_FAILED(rv)) return nsnull; return file; #else const char *arg = PR_GetEnv(name); if (!arg || !*arg) return nsnull; rv = NS_NewNativeLocalFile(nsDependentCString(arg), PR_TRUE, &file); if (NS_FAILED(rv)) return nsnull; return file; #endif } // Save the path of the given word to the specified environment variable // provided the environment variable does not have a value. static void SaveWordToEnvIfUnset(const char *name, const nsACString & word) { const char *val = PR_GetEnv(name); if (!(val && *val)) SaveWordToEnv(name, word); } // Save the path of the given file to the specified environment variable // provided the environment variable does not have a value. static void SaveFileToEnvIfUnset(const char *name, nsIFile *file) { const char *val = PR_GetEnv(name); if (!(val && *val)) SaveFileToEnv(name, file); } static PRBool strimatch(const char* lowerstr, const char* mixedstr) { while(*lowerstr) { if (!*mixedstr) return PR_FALSE; // mixedstr is shorter if (tolower(*mixedstr) != *lowerstr) return PR_FALSE; // no match ++lowerstr; ++mixedstr; } if (*mixedstr) return PR_FALSE; // lowerstr is shorter return PR_TRUE; } /** * Output a string to the user. This method is really only meant to be used to * output last-ditch error messages designed for developers NOT END USERS. * * @param isError * Pass true to indicate severe errors. * @param fmt * printf-style format string followed by arguments. */ static void Output(PRBool isError, const char *fmt, ... ) { va_list ap; va_start(ap, fmt); #if defined(XP_WIN) && !MOZ_WINCONSOLE char *msg = PR_vsmprintf(fmt, ap); if (msg) { UINT flags = MB_OK; if (isError) flags |= MB_ICONERROR; else flags |= MB_ICONINFORMATION; wchar_t wide_msg[1024]; MultiByteToWideChar(CP_ACP, 0, msg, -1, wide_msg, sizeof(wide_msg) / sizeof(wchar_t)); MessageBoxW(NULL, wide_msg, L"XULRunner", flags); PR_smprintf_free(msg); } #else vfprintf(stderr, fmt, ap); #endif va_end(ap); } enum RemoteResult { REMOTE_NOT_FOUND = 0, REMOTE_FOUND = 1, REMOTE_ARG_BAD = 2 }; enum ArgResult { ARG_NONE = 0, ARG_FOUND = 1, ARG_BAD = 2 // you wanted a param, but there isn't one }; static void RemoveArg(char **argv) { do { *argv = *(argv + 1); ++argv; } while (*argv); --gArgc; } /** * Check for a commandline flag. If the flag takes a parameter, the * parameter is returned in aParam. Flags may be in the form -arg or * --arg (or /arg on win32/OS2). * * @param aArg the parameter to check. Must be lowercase. * @param aCheckOSInt if true returns ARG_BAD if the osint argument is present * when aArg is also present. * @param if non-null, the -arg will be stored in this pointer. This is *not* * allocated, but rather a pointer to the argv data. */ static ArgResult CheckArg(const char* aArg, PRBool aCheckOSInt = PR_FALSE, const char **aParam = nsnull, PRBool aRemArg = PR_TRUE) { char **curarg = gArgv + 1; // skip argv[0] ArgResult ar = ARG_NONE; while (*curarg) { char *arg = curarg[0]; if (arg[0] == '-' #if defined(XP_WIN) || defined(XP_OS2) || *arg == '/' #endif ) { ++arg; if (*arg == '-') ++arg; if (strimatch(aArg, arg)) { if (aRemArg) RemoveArg(curarg); if (!aParam) { ar = ARG_FOUND; break; } if (*curarg) { if (**curarg == '-' #if defined(XP_WIN) || defined(XP_OS2) || **curarg == '/' #endif ) return ARG_BAD; *aParam = *curarg; if (aRemArg) RemoveArg(curarg); ar = ARG_FOUND; break; } return ARG_BAD; } } ++curarg; } if (aCheckOSInt && ar == ARG_FOUND) { ArgResult arOSInt = CheckArg("osint"); if (arOSInt == ARG_FOUND) { ar = ARG_BAD; PR_fprintf(PR_STDERR, "Error: argument -osint is invalid\n"); } } return ar; } #if defined(XP_WIN) /** * Check for a commandline flag from the windows shell and remove it from the * argv used when restarting. Flags MUST be in the form -arg. * * @param aArg the parameter to check. Must be lowercase. */ static ArgResult CheckArgShell(const char* aArg) { char **curarg = gRestartArgv + 1; // skip argv[0] while (*curarg) { char *arg = curarg[0]; if (arg[0] == '-') { ++arg; if (strimatch(aArg, arg)) { do { *curarg = *(curarg + 1); ++curarg; } while (*curarg); --gRestartArgc; return ARG_FOUND; } } ++curarg; } return ARG_NONE; } /** * Enabled Native App Support to process DDE messages when the app needs to * restart and the app has been launched by the Windows shell to open an url. * When aWait is false this will process the DDE events manually. This prevents * Windows from displaying an error message due to the DDE message not being * acknowledged. */ static void ProcessDDE(nsINativeAppSupport* aNative, PRBool aWait) { // When the app is launched by the windows shell the windows shell // expects the app to be available for DDE messages and if it isn't // windows displays an error dialog. To prevent the error the DDE server // is enabled and pending events are processed when the app needs to // restart after it was launched by the shell with the requestpending // argument. The requestpending pending argument is removed to // differentiate it from being launched when an app restart is not // required. ArgResult ar; ar = CheckArgShell("requestpending"); if (ar == ARG_FOUND) { aNative->Enable(); // enable win32 DDE responses if (aWait) { nsIThread *thread = NS_GetCurrentThread(); // This is just a guesstimate based on testing different values. // If count is 8 or less windows will display an error dialog. PRInt32 count = 20; while(--count >= 0) { NS_ProcessNextEvent(thread); PR_Sleep(PR_MillisecondsToInterval(1)); } } } } #endif PRBool gSafeMode = PR_FALSE; /** * The nsXULAppInfo object implements nsIFactory so that it can be its own * singleton. */ class nsXULAppInfo : public nsIXULAppInfo, #ifdef XP_WIN public nsIWinAppHelper, #endif #ifdef MOZ_CRASHREPORTER public nsICrashReporter, #endif public nsIXULRuntime { public: NS_DECL_ISUPPORTS_INHERITED NS_DECL_NSIXULAPPINFO NS_DECL_NSIXULRUNTIME #ifdef MOZ_CRASHREPORTER NS_DECL_NSICRASHREPORTER #endif #ifdef XP_WIN NS_DECL_NSIWINAPPHELPER #endif }; NS_INTERFACE_MAP_BEGIN(nsXULAppInfo) NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIXULRuntime) NS_INTERFACE_MAP_ENTRY(nsIXULRuntime) #ifdef XP_WIN NS_INTERFACE_MAP_ENTRY(nsIWinAppHelper) #endif #ifdef MOZ_CRASHREPORTER NS_INTERFACE_MAP_ENTRY(nsICrashReporter) #endif NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIXULAppInfo, gAppData) NS_INTERFACE_MAP_END NS_IMETHODIMP_(nsrefcnt) nsXULAppInfo::AddRef() { return 1; } NS_IMETHODIMP_(nsrefcnt) nsXULAppInfo::Release() { return 1; } NS_IMETHODIMP nsXULAppInfo::GetVendor(nsACString& aResult) { aResult.Assign(gAppData->vendor); return NS_OK; } NS_IMETHODIMP nsXULAppInfo::GetName(nsACString& aResult) { aResult.Assign(gAppData->name); return NS_OK; } NS_IMETHODIMP nsXULAppInfo::GetID(nsACString& aResult) { aResult.Assign(gAppData->ID); return NS_OK; } NS_IMETHODIMP nsXULAppInfo::GetVersion(nsACString& aResult) { aResult.Assign(gAppData->version); return NS_OK; } NS_IMETHODIMP nsXULAppInfo::GetPlatformVersion(nsACString& aResult) { aResult.Assign(gToolkitVersion); return NS_OK; } NS_IMETHODIMP nsXULAppInfo::GetAppBuildID(nsACString& aResult) { aResult.Assign(gAppData->buildID); return NS_OK; } NS_IMETHODIMP nsXULAppInfo::GetPlatformBuildID(nsACString& aResult) { aResult.Assign(gToolkitBuildID); return NS_OK; } NS_IMETHODIMP nsXULAppInfo::GetLogConsoleErrors(PRBool *aResult) { *aResult = gLogConsoleErrors; return NS_OK; } NS_IMETHODIMP nsXULAppInfo::SetLogConsoleErrors(PRBool aValue) { gLogConsoleErrors = aValue; return NS_OK; } NS_IMETHODIMP nsXULAppInfo::GetInSafeMode(PRBool *aResult) { *aResult = gSafeMode; return NS_OK; } NS_IMETHODIMP nsXULAppInfo::GetOS(nsACString& aResult) { aResult.AssignLiteral(OS_TARGET); return NS_OK; } NS_IMETHODIMP nsXULAppInfo::GetXPCOMABI(nsACString& aResult) { #ifdef TARGET_XPCOM_ABI aResult.AssignLiteral(TARGET_XPCOM_ABI); return NS_OK; #else return NS_ERROR_NOT_AVAILABLE; #endif } NS_IMETHODIMP nsXULAppInfo::GetWidgetToolkit(nsACString& aResult) { aResult.AssignLiteral(MOZ_WIDGET_TOOLKIT); return NS_OK; } // Ensure that the GeckoProcessType enum, defined in xpcom/build/nsXULAppAPI.h, // is synchronized with the const unsigned longs defined in // xpcom/system/nsIXULRuntime.idl. #define SYNC_ENUMS(a,b) \ PR_STATIC_ASSERT(nsIXULRuntime::PROCESS_TYPE_ ## a == \ static_cast(GeckoProcessType_ ## b)); SYNC_ENUMS(DEFAULT, Default) SYNC_ENUMS(PLUGIN, Plugin) SYNC_ENUMS(CONTENT, Content) SYNC_ENUMS(IPDLUNITTEST, IPDLUnitTest) // .. and ensure that that is all of them: PR_STATIC_ASSERT(GeckoProcessType_IPDLUnitTest + 1 == GeckoProcessType_End); NS_IMETHODIMP nsXULAppInfo::GetProcessType(PRUint32* aResult) { NS_ENSURE_ARG_POINTER(aResult); *aResult = XRE_GetProcessType(); return NS_OK; } NS_IMETHODIMP nsXULAppInfo::InvalidateCachesOnRestart() { nsCOMPtr file; nsresult rv = NS_GetSpecialDirectory(NS_APP_PROFILE_DIR_STARTUP, getter_AddRefs(file)); if (NS_FAILED(rv)) return rv; if (!file) return NS_ERROR_NOT_AVAILABLE; file->AppendNative(FILE_COMPATIBILITY_INFO); nsCOMPtr localFile(do_QueryInterface(file)); nsINIParser parser; rv = parser.Init(localFile); if (NS_FAILED(rv)) { // This fails if compatibility.ini is not there, so we'll // flush the caches on the next restart anyways. return NS_OK; } nsCAutoString buf; rv = parser.GetString("Compatibility", "InvalidateCaches", buf); if (NS_FAILED(rv)) { PRFileDesc *fd = nsnull; localFile->OpenNSPRFileDesc(PR_RDWR | PR_APPEND, 0600, &fd); if (!fd) { NS_ERROR("could not create output stream"); return NS_ERROR_NOT_AVAILABLE; } static const char kInvalidationHeader[] = NS_LINEBREAK "InvalidateCaches=1" NS_LINEBREAK; rv = PR_Write(fd, kInvalidationHeader, sizeof(kInvalidationHeader) - 1); PR_Close(fd); if (NS_FAILED(rv)) return rv; } return NS_OK; } #ifdef XP_WIN // Matches the enum in WinNT.h for the Vista SDK but renamed so that we can // safely build with the Vista SDK and without it. typedef enum { VistaTokenElevationTypeDefault = 1, VistaTokenElevationTypeFull, VistaTokenElevationTypeLimited } VISTA_TOKEN_ELEVATION_TYPE; // avoid collision with TokeElevationType enum in WinNT.h // of the Vista SDK #define VistaTokenElevationType static_cast< TOKEN_INFORMATION_CLASS >( 18 ) NS_IMETHODIMP nsXULAppInfo::GetUserCanElevate(PRBool *aUserCanElevate) { #ifdef WINCE *aUserCanElevate = PR_FALSE; return NS_OK; #else HANDLE hToken; VISTA_TOKEN_ELEVATION_TYPE elevationType; DWORD dwSize; if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hToken) || !GetTokenInformation(hToken, VistaTokenElevationType, &elevationType, sizeof(elevationType), &dwSize)) { *aUserCanElevate = PR_FALSE; } else { // The possible values returned for elevationType and their meanings are: // TokenElevationTypeDefault: The token does not have a linked token // (e.g. UAC disabled or a standard user, so they can't be elevated) // TokenElevationTypeFull: The token is linked to an elevated token // (e.g. UAC is enabled and the user is already elevated so they can't // be elevated again) // TokenElevationTypeLimited: The token is linked to a limited token // (e.g. UAC is enabled and the user is not elevated, so they can be // elevated) *aUserCanElevate = (elevationType == VistaTokenElevationTypeLimited); } if (hToken) CloseHandle(hToken); return NS_OK; #endif // WINCE } #endif #ifdef MOZ_CRASHREPORTER NS_IMETHODIMP nsXULAppInfo::GetEnabled(PRBool *aEnabled) { *aEnabled = CrashReporter::GetEnabled(); return NS_OK; } NS_IMETHODIMP nsXULAppInfo::SetEnabled(PRBool aEnabled) { if (aEnabled) { if (CrashReporter::GetEnabled()) // no point in erroring for double-enabling return NS_OK; nsCOMPtr xreDirectory; if (gAppData) { xreDirectory = gAppData->xreDirectory; } else { // We didn't get started through XRE_Main, probably nsCOMPtr greDir; NS_GetSpecialDirectory(NS_GRE_DIR, getter_AddRefs(greDir)); if (!greDir) return NS_ERROR_FAILURE; xreDirectory = do_QueryInterface(greDir); if (!xreDirectory) return NS_ERROR_FAILURE; } return CrashReporter::SetExceptionHandler(xreDirectory, true); } else { if (!CrashReporter::GetEnabled()) // no point in erroring for double-disabling return NS_OK; return CrashReporter::UnsetExceptionHandler(); } } NS_IMETHODIMP nsXULAppInfo::GetServerURL(nsIURL** aServerURL) { if (!CrashReporter::GetEnabled()) return NS_ERROR_NOT_INITIALIZED; nsCAutoString data; if (!CrashReporter::GetServerURL(data)) { return NS_ERROR_FAILURE; } nsCOMPtr uri; NS_NewURI(getter_AddRefs(uri), data); if (!uri) return NS_ERROR_FAILURE; nsCOMPtr url; url = do_QueryInterface(uri); NS_ADDREF(*aServerURL = url); return NS_OK; } NS_IMETHODIMP nsXULAppInfo::SetServerURL(nsIURL* aServerURL) { PRBool schemeOk; // only allow https or http URLs nsresult rv = aServerURL->SchemeIs("https", &schemeOk); NS_ENSURE_SUCCESS(rv, rv); if (!schemeOk) { rv = aServerURL->SchemeIs("http", &schemeOk); NS_ENSURE_SUCCESS(rv, rv); if (!schemeOk) return NS_ERROR_INVALID_ARG; } nsCAutoString spec; rv = aServerURL->GetSpec(spec); NS_ENSURE_SUCCESS(rv, rv); return CrashReporter::SetServerURL(spec); } NS_IMETHODIMP nsXULAppInfo::GetMinidumpPath(nsILocalFile** aMinidumpPath) { if (!CrashReporter::GetEnabled()) return NS_ERROR_NOT_INITIALIZED; nsAutoString path; if (!CrashReporter::GetMinidumpPath(path)) return NS_ERROR_FAILURE; nsresult rv = NS_NewLocalFile(path, PR_FALSE, aMinidumpPath); NS_ENSURE_SUCCESS(rv, rv); return NS_OK; } NS_IMETHODIMP nsXULAppInfo::SetMinidumpPath(nsILocalFile* aMinidumpPath) { nsAutoString path; nsresult rv = aMinidumpPath->GetPath(path); NS_ENSURE_SUCCESS(rv, rv); return CrashReporter::SetMinidumpPath(path); } NS_IMETHODIMP nsXULAppInfo::AnnotateCrashReport(const nsACString& key, const nsACString& data) { return CrashReporter::AnnotateCrashReport(key, data); } NS_IMETHODIMP nsXULAppInfo::AppendAppNotesToCrashReport(const nsACString& data) { return CrashReporter::AppendAppNotesToCrashReport(data); } NS_IMETHODIMP nsXULAppInfo::WriteMinidumpForException(void* aExceptionInfo) { #ifdef XP_WIN32 return CrashReporter::WriteMinidumpForException(static_cast(aExceptionInfo)); #else return NS_ERROR_NOT_IMPLEMENTED; #endif } NS_IMETHODIMP nsXULAppInfo::AppendObjCExceptionInfoToAppNotes(void* aException) { #ifdef XP_MACOSX return CrashReporter::AppendObjCExceptionInfoToAppNotes(aException); #else return NS_ERROR_NOT_IMPLEMENTED; #endif } NS_IMETHODIMP nsXULAppInfo::GetSubmitReports(PRBool* aEnabled) { return CrashReporter::GetSubmitReports(aEnabled); } NS_IMETHODIMP nsXULAppInfo::SetSubmitReports(PRBool aEnabled) { return CrashReporter::SetSubmitReports(aEnabled); } #endif static const nsXULAppInfo kAppInfo; static NS_METHOD AppInfoConstructor(nsISupports* aOuter, REFNSIID aIID, void **aResult) { NS_ENSURE_NO_AGGREGATION(aOuter); return const_cast(&kAppInfo)-> QueryInterface(aIID, aResult); } PRBool gLogConsoleErrors #ifdef DEBUG = PR_TRUE; #else = PR_FALSE; #endif #define NS_ENSURE_TRUE_LOG(x, ret) \ PR_BEGIN_MACRO \ if (NS_UNLIKELY(!(x))) { \ NS_WARNING("NS_ENSURE_TRUE(" #x ") failed"); \ gLogConsoleErrors = PR_TRUE; \ return ret; \ } \ PR_END_MACRO #define NS_ENSURE_SUCCESS_LOG(res, ret) \ NS_ENSURE_TRUE_LOG(NS_SUCCEEDED(res), ret) /** * Because we're starting/stopping XPCOM several times in different scenarios, * this class is a stack-based critter that makes sure that XPCOM is shut down * during early returns. */ class ScopedXPCOMStartup { public: ScopedXPCOMStartup() : mServiceManager(nsnull) { } ~ScopedXPCOMStartup(); nsresult Initialize(); nsresult DoAutoreg(); nsresult RegisterProfileService(); nsresult SetWindowCreator(nsINativeAppSupport* native); private: nsIServiceManager* mServiceManager; }; ScopedXPCOMStartup::~ScopedXPCOMStartup() { if (mServiceManager) { #ifdef XP_MACOSX // On OS X, we need a pool to catch cocoa objects that are autoreleased // during teardown. mozilla::MacAutoreleasePool pool; #endif nsCOMPtr appStartup (do_GetService(NS_APPSTARTUP_CONTRACTID)); if (appStartup) appStartup->DestroyHiddenWindow(); gDirServiceProvider->DoShutdown(); WriteConsoleLog(); NS_ShutdownXPCOM(mServiceManager); mServiceManager = nsnull; #ifdef MOZ_OMNIJAR mozilla::SetOmnijar(nsnull); #endif } } // {95d89e3e-a169-41a3-8e56-719978e15b12} #define APPINFO_CID \ { 0x95d89e3e, 0xa169, 0x41a3, { 0x8e, 0x56, 0x71, 0x99, 0x78, 0xe1, 0x5b, 0x12 } } static const nsModuleComponentInfo kComponents[] = { { "nsXULAppInfo", APPINFO_CID, XULAPPINFO_SERVICE_CONTRACTID, AppInfoConstructor }, { "nsXULAppInfo", APPINFO_CID, XULRUNTIME_SERVICE_CONTRACTID, AppInfoConstructor } #ifdef MOZ_CRASHREPORTER , { "nsXULAppInfo", APPINFO_CID, NS_CRASHREPORTER_CONTRACTID, AppInfoConstructor } #endif }; NS_IMPL_NSGETMODULE(Apprunner, kComponents) #if !defined(_BUILD_STATIC_BIN) && !defined(MOZ_ENABLE_LIBXUL) static nsStaticModuleInfo const kXREStaticModules[] = { { "Apprunner", Apprunner_NSGetModule } }; nsStaticModuleInfo const *const kPStaticModules = kXREStaticModules; PRUint32 const kStaticModuleCount = NS_ARRAY_LENGTH(kXREStaticModules); #endif nsresult ScopedXPCOMStartup::Initialize() { NS_ASSERTION(gDirServiceProvider, "Should not get here!"); nsresult rv; #ifdef MOZ_OMNIJAR nsCOMPtr lf; rv = XRE_GetBinaryPath(gArgv[0], getter_AddRefs(lf)); if (NS_SUCCEEDED(rv)) mozilla::SetOmnijar(lf); #endif rv = NS_InitXPCOM3(&mServiceManager, gDirServiceProvider->GetAppDir(), gDirServiceProvider, kPStaticModules, kStaticModuleCount); if (NS_FAILED(rv)) { NS_ERROR("Couldn't start xpcom!"); mServiceManager = nsnull; } else { nsCOMPtr reg = do_QueryInterface(mServiceManager); NS_ASSERTION(reg, "Service Manager doesn't QI to Registrar."); } return rv; } // {0C4A446C-EE82-41f2-8D04-D366D2C7A7D4} static const nsCID kNativeAppSupportCID = { 0xc4a446c, 0xee82, 0x41f2, { 0x8d, 0x4, 0xd3, 0x66, 0xd2, 0xc7, 0xa7, 0xd4 } }; // {5F5E59CE-27BC-47eb-9D1F-B09CA9049836} static const nsCID kProfileServiceCID = { 0x5f5e59ce, 0x27bc, 0x47eb, { 0x9d, 0x1f, 0xb0, 0x9c, 0xa9, 0x4, 0x98, 0x36 } }; nsresult ScopedXPCOMStartup::RegisterProfileService() { NS_ASSERTION(mServiceManager, "Not initialized!"); nsCOMPtr factory; NS_NewToolkitProfileFactory(getter_AddRefs(factory)); if (!factory) return NS_ERROR_OUT_OF_MEMORY; nsCOMPtr reg (do_QueryInterface(mServiceManager)); if (!reg) return NS_ERROR_NO_INTERFACE; return reg->RegisterFactory(kProfileServiceCID, "Toolkit Profile Service", NS_PROFILESERVICE_CONTRACTID, factory); } nsresult ScopedXPCOMStartup::DoAutoreg() { #ifdef DEBUG // _Always_ autoreg if we're in a debug build, under the assumption // that people are busily modifying components and will be angry if // their changes aren't noticed. nsCOMPtr registrar (do_QueryInterface(mServiceManager)); NS_ASSERTION(registrar, "Where's the component registrar?"); registrar->AutoRegister(nsnull); #endif return NS_OK; } /** * This is a little factory class that serves as a singleton-service-factory * for the nativeappsupport object. */ class nsSingletonFactory : public nsIFactory { public: NS_DECL_ISUPPORTS NS_DECL_NSIFACTORY nsSingletonFactory(nsISupports* aSingleton); ~nsSingletonFactory() { } private: nsCOMPtr mSingleton; }; nsSingletonFactory::nsSingletonFactory(nsISupports* aSingleton) : mSingleton(aSingleton) { NS_ASSERTION(mSingleton, "Singleton was null!"); } NS_IMPL_ISUPPORTS1(nsSingletonFactory, nsIFactory) NS_IMETHODIMP nsSingletonFactory::CreateInstance(nsISupports* aOuter, const nsIID& aIID, void* *aResult) { NS_ENSURE_NO_AGGREGATION(aOuter); return mSingleton->QueryInterface(aIID, aResult); } NS_IMETHODIMP nsSingletonFactory::LockFactory(PRBool) { return NS_OK; } /** * Set our windowcreator on the WindowWatcher service. */ nsresult ScopedXPCOMStartup::SetWindowCreator(nsINativeAppSupport* native) { NS_TIME_FUNCTION; nsresult rv; nsCOMPtr registrar (do_QueryInterface(mServiceManager)); NS_ASSERTION(registrar, "Where's the component registrar?"); nsCOMPtr nativeFactory = new nsSingletonFactory(native); NS_ENSURE_TRUE(nativeFactory, NS_ERROR_OUT_OF_MEMORY); rv = registrar->RegisterFactory(kNativeAppSupportCID, "Native App Support", NS_NATIVEAPPSUPPORT_CONTRACTID, nativeFactory); NS_ENSURE_SUCCESS(rv, rv); NS_TIME_FUNCTION_MARK("RegisterFactory done"); // Inform the chrome registry about OS accessibility nsCOMPtr cr = mozilla::services::GetToolkitChromeRegistryService(); NS_TIME_FUNCTION_MARK("Got ToolkitChromeRegistry service"); if (cr) cr->CheckForOSAccessibility(); NS_TIME_FUNCTION_MARK("OS Accessibility check"); nsCOMPtr creator (do_GetService(NS_APPSTARTUP_CONTRACTID)); if (!creator) return NS_ERROR_UNEXPECTED; NS_TIME_FUNCTION_MARK("Got AppStartup service"); nsCOMPtr wwatch (do_GetService(NS_WINDOWWATCHER_CONTRACTID, &rv)); NS_ENSURE_SUCCESS(rv, rv); NS_TIME_FUNCTION_MARK("Got WindowWatcher service"); return wwatch->SetWindowCreator(creator); } /** * A helper class which calls NS_LogInit/NS_LogTerm in its scope. */ class ScopedLogging { public: ScopedLogging() { NS_LogInit(); } ~ScopedLogging() { NS_LogTerm(); } }; static void DumpArbitraryHelp() { nsresult rv; ScopedLogging log; { ScopedXPCOMStartup xpcom; xpcom.Initialize(); xpcom.DoAutoreg(); nsCOMPtr cmdline (do_CreateInstance("@mozilla.org/toolkit/command-line;1")); if (!cmdline) return; nsCString text; rv = cmdline->GetHelpText(text); if (NS_SUCCEEDED(rv)) printf("%s", text.get()); } } // English text needs to go into a dtd file. // But when this is called we have no components etc. These strings must either be // here, or in a native resource file. static void DumpHelp() { printf("Usage: %s [ options ... ] [URL]\n" " where options include:\n\n", gArgv[0]); #ifdef MOZ_X11 printf("X11 options\n" " --display=DISPLAY X display to use\n" " --sync Make X calls synchronous\n" " --no-xshm Don't use X shared memory extension\n" " --xim-preedit=STYLE\n" " --xim-status=STYLE\n"); #endif #ifdef XP_UNIX printf(" --g-fatal-warnings Make all warnings fatal\n" "\n%s options\n", gAppData->name); #endif printf(" -h or -help Print this message.\n" " -v or -version Print %s version.\n" " -P Start with .\n" " -migration Start with migration wizard.\n" " -ProfileManager Start with ProfileManager.\n" " -no-remote Open new instance, not a new window in running instance.\n" " -UILocale Start with resources as UI Locale.\n" " -safe-mode Disables extensions and themes for this session.\n", gAppData->name); #if defined(XP_WIN) || defined(XP_OS2) printf(" -console Start %s with a debugging console.\n", gAppData->name); #endif // this works, but only after the components have registered. so if you drop in a new command line handler, -help // won't not until the second run. // out of the bug, because we ship a component.reg file, it works correctly. DumpArbitraryHelp(); } #ifdef DEBUG_warren #ifdef XP_WIN #define _CRTDBG_MAP_ALLOC #include #endif #endif #if defined(FREEBSD) // pick up fpsetmask prototype. #include #endif static inline void DumpVersion() { printf("%s %s %s", gAppData->vendor ? gAppData->vendor : "", gAppData->name, gAppData->version); if (gAppData->copyright) printf(", %s", gAppData->copyright); printf("\n"); } #ifdef MOZ_ENABLE_XREMOTE // use int here instead of a PR type since it will be returned // from main - just to keep types consistent static int HandleRemoteArgument(const char* remote, const char* aDesktopStartupID) { nsresult rv; ArgResult ar; const char *profile = 0; nsCAutoString program(gAppData->name); ToLowerCase(program); const char *username = getenv("LOGNAME"); ar = CheckArg("p", PR_FALSE, &profile); if (ar == ARG_BAD) { PR_fprintf(PR_STDERR, "Error: argument -p requires a profile name\n"); return 1; } const char *temp = nsnull; ar = CheckArg("a", PR_FALSE, &temp); if (ar == ARG_BAD) { PR_fprintf(PR_STDERR, "Error: argument -a requires an application name\n"); return 1; } else if (ar == ARG_FOUND) { program.Assign(temp); } ar = CheckArg("u", PR_FALSE, &username); if (ar == ARG_BAD) { PR_fprintf(PR_STDERR, "Error: argument -u requires a username\n"); return 1; } XRemoteClient client; rv = client.Init(); if (NS_FAILED(rv)) { PR_fprintf(PR_STDERR, "Error: Failed to connect to X server.\n"); return 1; } nsXPIDLCString response; PRBool success = PR_FALSE; rv = client.SendCommand(program.get(), username, profile, remote, aDesktopStartupID, getter_Copies(response), &success); // did the command fail? if (NS_FAILED(rv)) { PR_fprintf(PR_STDERR, "Error: Failed to send command: %s\n", response ? response.get() : "No response included"); return 1; } if (!success) { PR_fprintf(PR_STDERR, "Error: No running window found\n"); return 2; } return 0; } static RemoteResult RemoteCommandLine(const char* aDesktopStartupID) { nsresult rv; ArgResult ar; nsCAutoString program(gAppData->name); ToLowerCase(program); const char *username = getenv("LOGNAME"); const char *temp = nsnull; ar = CheckArg("a", PR_TRUE, &temp); if (ar == ARG_BAD) { PR_fprintf(PR_STDERR, "Error: argument -a requires an application name\n"); return REMOTE_ARG_BAD; } else if (ar == ARG_FOUND) { program.Assign(temp); } ar = CheckArg("u", PR_TRUE, &username); if (ar == ARG_BAD) { PR_fprintf(PR_STDERR, "Error: argument -u requires a username\n"); return REMOTE_ARG_BAD; } XRemoteClient client; rv = client.Init(); if (NS_FAILED(rv)) return REMOTE_NOT_FOUND; nsXPIDLCString response; PRBool success = PR_FALSE; rv = client.SendCommandLine(program.get(), username, nsnull, gArgc, gArgv, aDesktopStartupID, getter_Copies(response), &success); // did the command fail? if (NS_FAILED(rv) || !success) return REMOTE_NOT_FOUND; return REMOTE_FOUND; } #endif // MOZ_ENABLE_XREMOTE #ifdef XP_MACOSX static char const *gBinaryPath; #endif nsresult XRE_GetBinaryPath(const char* argv0, nsILocalFile* *aResult) { nsresult rv; nsCOMPtr lf; // We need to use platform-specific hackery to find the // path of this executable. This is copied, with some modifications, from // nsGREDirServiceProvider.cpp #ifdef XP_WIN PRUnichar exePath[MAXPATHLEN]; if (!::GetModuleFileNameW(0, exePath, MAXPATHLEN)) return NS_ERROR_FAILURE; rv = NS_NewLocalFile(nsDependentString(exePath), PR_TRUE, getter_AddRefs(lf)); if (NS_FAILED(rv)) return rv; #elif defined(XP_MACOSX) if (gBinaryPath) return NS_NewNativeLocalFile(nsDependentCString(gBinaryPath), PR_FALSE, aResult); NS_NewNativeLocalFile(EmptyCString(), PR_TRUE, getter_AddRefs(lf)); nsCOMPtr lfm (do_QueryInterface(lf)); if (!lfm) return NS_ERROR_FAILURE; // Works even if we're not bundled. CFBundleRef appBundle = CFBundleGetMainBundle(); if (!appBundle) return NS_ERROR_FAILURE; CFURLRef bundleURL = CFBundleCopyExecutableURL(appBundle); if (!bundleURL) return NS_ERROR_FAILURE; FSRef fileRef; if (!CFURLGetFSRef(bundleURL, &fileRef)) { CFRelease(bundleURL); return NS_ERROR_FAILURE; } rv = lfm->InitWithFSRef(&fileRef); CFRelease(bundleURL); if (NS_FAILED(rv)) return rv; #elif defined(XP_UNIX) struct stat fileStat; char exePath[MAXPATHLEN]; char tmpPath[MAXPATHLEN]; rv = NS_ERROR_FAILURE; // on unix, there is no official way to get the path of the current binary. // instead of using the MOZILLA_FIVE_HOME hack, which doesn't scale to // multiple applications, we will try a series of techniques: // // 1) look for /proc//exe which is a symlink to the executable on newer // Linux kernels // 2) use realpath() on argv[0], which works unless we're loaded from the // PATH // 3) manually walk through the PATH and look for ourself // 4) give up // #ifdef __linux__ // Commented out because it used to not work because it used to not deal // with readlink not null-terminating the buffer. #if 0 int r = readlink("/proc/self/exe", exePath, MAXPATHLEN); if (r > 0 && r < MAXPATHLEN) { exePath[r] = '\0'; if (stat(exePath, &fileStat) == 0) { rv = NS_OK; } } #endif if (NS_FAILED(rv) && realpath(argv0, exePath) && stat(exePath, &fileStat) == 0) { rv = NS_OK; } if (NS_FAILED(rv)) { const char *path = getenv("PATH"); if (!path) return NS_ERROR_FAILURE; char *pathdup = strdup(path); if (!pathdup) return NS_ERROR_OUT_OF_MEMORY; PRBool found = PR_FALSE; char *newStr = pathdup; char *token; while ( (token = nsCRT::strtok(newStr, ":", &newStr)) ) { sprintf(tmpPath, "%s/%s", token, argv0); if (realpath(tmpPath, exePath) && stat(exePath, &fileStat) == 0) { found = PR_TRUE; break; } } free(pathdup); if (!found) return NS_ERROR_FAILURE; } rv = NS_NewNativeLocalFile(nsDependentCString(exePath), PR_TRUE, getter_AddRefs(lf)); if (NS_FAILED(rv)) return rv; #elif defined(XP_OS2) PPIB ppib; PTIB ptib; char exePath[MAXPATHLEN]; DosGetInfoBlocks( &ptib, &ppib); DosQueryModuleName( ppib->pib_hmte, MAXPATHLEN, exePath); rv = NS_NewNativeLocalFile(nsDependentCString(exePath), PR_TRUE, getter_AddRefs(lf)); if (NS_FAILED(rv)) return rv; #elif defined(XP_BEOS) int32 cookie = 0; image_info info; if(get_next_image_info(0, &cookie, &info) != B_OK) return NS_ERROR_FAILURE; rv = NS_NewNativeLocalFile(nsDependentCString(info.name), PR_TRUE, getter_AddRefs(lf)); if (NS_FAILED(rv)) return rv; #else #error Oops, you need platform-specific code here #endif NS_ADDREF(*aResult = lf); return NS_OK; } #define NS_ERROR_LAUNCHED_CHILD_PROCESS NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_PROFILE, 200) #ifdef XP_WIN #include "nsWindowsRestart.cpp" #include #endif #if defined(XP_OS2) && (__KLIBC__ == 0 && __KLIBC_MINOR__ >= 6) // broken kLibc // Copy the environment maintained by the C library into an ASCIIZ array // that can be used to pass it on to the OS/2 Dos* APIs (which otherwise // don't know anything about the stuff set by PR_SetEnv() or setenv()). char *createEnv() { // just allocate the maximum amount (24 kB = 0x60000 bytes), to be able to // copy the existing environment char *env = (char *)calloc(0x6000, sizeof(char)); if (!env) { return NULL; } // walk along the environ string array of the C library and copy // everything (that fits) into the output environment array, leaving // null bytes between the entries char *penv = env; // movable pointer to result environment ASCIIZ array int i = 0, space = 0x6000; while (environ[i] && environ[i][0]) { int len = strlen(environ[i]); if (space - len <= 0) { break; } strcpy(penv, environ[i]); i++; // next environment variable penv += len + 1; // jump to after next null byte space -= len - 1; // subtract consumed length from usable space } return env; } // OS2LaunchChild() is there to replace _execv() which is broken in the C // runtime library that comes with GCC 3.3.5 on OS/2. It uses createEnv() // to copy the process environment and add necessary variables // // returns -1 on failure and 0 on success int OS2LaunchChild(const char *aExePath, int aArgc, char **aArgv) { // find total length of aArgv int len = 0; for (int i = 0; i < aArgc; i++) { len += strlen(aArgv[i]) + 1; // plus space in between } len++; // leave space for null byte at end // allocate enough space for all strings and nulls, // calloc helpfully initializes to null char *args = (char *)calloc(len, sizeof(char)); if (!args) { return -1; } char *pargs = args; // extra pointer to after the last argument // build argument list in the format the DosStartSession() wants, // adding spaces between the arguments for (int i = 0; i < aArgc; i++, *pargs++ = ' ') { strcpy(pargs, aArgv[i]); pargs += strlen(aArgv[i]); } if (aArgc > 1) { *(pargs-1) = '\0'; // replace last space } *pargs = '\0'; // make sure that the program is separated by null byte pargs = strchr(args, ' '); if (pargs) { *pargs = '\0'; } char *env = createEnv(); char error[CCHMAXPATH] = { 0 }; RESULTCODES crc = { 0 }; ULONG rc = DosExecPgm(error, sizeof(error), EXEC_ASYNC, args, env, &crc, (PSZ)aExePath); free(args); // done with the arguments if (env) { free(env); } if (rc != NO_ERROR) { return -1; } return 0; } #endif // If aBlankCommandLine is true, then the application will be launched with a // blank command line instead of being launched with the same command line that // it was initially started with. static nsresult LaunchChild(nsINativeAppSupport* aNative, PRBool aBlankCommandLine = PR_FALSE) { aNative->Quit(); // release DDE mutex, if we're holding it // Restart this process by exec'ing it into the current process // if supported by the platform. Otherwise, use NSPR. if (aBlankCommandLine) { gRestartArgc = 1; gRestartArgv[gRestartArgc] = nsnull; } SaveToEnv("MOZ_LAUNCHED_CHILD=1"); #if defined(ANDROID) mozilla::AndroidBridge::Bridge()->ScheduleRestart(); #else #if defined(XP_MACOSX) CommandLineServiceMac::SetupMacCommandLine(gRestartArgc, gRestartArgv, PR_TRUE); LaunchChildMac(gRestartArgc, gRestartArgv); #else nsCOMPtr lf; nsresult rv = XRE_GetBinaryPath(gArgv[0], getter_AddRefs(lf)); if (NS_FAILED(rv)) return rv; #if defined(XP_WIN) nsAutoString exePath; rv = lf->GetPath(exePath); if (NS_FAILED(rv)) return rv; if (!WinLaunchChild(exePath.get(), gRestartArgc, gRestartArgv)) return NS_ERROR_FAILURE; #else nsCAutoString exePath; rv = lf->GetNativePath(exePath); if (NS_FAILED(rv)) return rv; #if defined(XP_OS2) && (__KLIBC__ == 0 && __KLIBC_MINOR__ >= 6) // implementation of _execv() is broken with kLibc 0.6.x and later if (OS2LaunchChild(exePath.get(), gRestartArgc, gRestartArgv) == -1) return NS_ERROR_FAILURE; #elif defined(XP_OS2) if (_execv(exePath.get(), gRestartArgv) == -1) return NS_ERROR_FAILURE; #elif defined(XP_UNIX) if (execv(exePath.get(), gRestartArgv) == -1) return NS_ERROR_FAILURE; #elif defined(XP_BEOS) extern char **environ; status_t res; res = resume_thread(load_image(gRestartArgc,(const char **)gRestartArgv,(const char **)environ)); if (res != B_OK) return NS_ERROR_FAILURE; #else PRProcess* process = PR_CreateProcess(exePath.get(), gRestartArgv, nsnull, nsnull); if (!process) return NS_ERROR_FAILURE; PRInt32 exitCode; PRStatus failed = PR_WaitProcess(process, &exitCode); if (failed || exitCode) return NS_ERROR_FAILURE; #endif // XP_OS2 series #endif // WP_WIN #endif // WP_MACOSX #endif // ANDROID return NS_ERROR_LAUNCHED_CHILD_PROCESS; } static const char kProfileProperties[] = "chrome://mozapps/locale/profile/profileSelection.properties"; static nsresult ProfileLockedDialog(nsILocalFile* aProfileDir, nsILocalFile* aProfileLocalDir, nsIProfileUnlocker* aUnlocker, nsINativeAppSupport* aNative, nsIProfileLock* *aResult) { nsresult rv; ScopedXPCOMStartup xpcom; rv = xpcom.Initialize(); NS_ENSURE_SUCCESS(rv, rv); rv = xpcom.DoAutoreg(); rv |= xpcom.SetWindowCreator(aNative); NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE); { //extra scoping is needed so we release these components before xpcom shutdown nsCOMPtr sbs = mozilla::services::GetStringBundleService(); NS_ENSURE_TRUE(sbs, NS_ERROR_FAILURE); nsCOMPtr sb; sbs->CreateBundle(kProfileProperties, getter_AddRefs(sb)); NS_ENSURE_TRUE_LOG(sbs, NS_ERROR_FAILURE); NS_ConvertUTF8toUTF16 appName(gAppData->name); const PRUnichar* params[] = {appName.get(), appName.get()}; nsXPIDLString killMessage; #ifndef XP_MACOSX static const PRUnichar kRestartNoUnlocker[] = {'r','e','s','t','a','r','t','M','e','s','s','a','g','e','N','o','U','n','l','o','c','k','e','r','\0'}; // "restartMessageNoUnlocker" static const PRUnichar kRestartUnlocker[] = {'r','e','s','t','a','r','t','M','e','s','s','a','g','e','U','n','l','o','c','k','e','r','\0'}; // "restartMessageUnlocker" #else static const PRUnichar kRestartNoUnlocker[] = {'r','e','s','t','a','r','t','M','e','s','s','a','g','e','N','o','U','n','l','o','c','k','e','r','M','a','c','\0'}; // "restartMessageNoUnlockerMac" static const PRUnichar kRestartUnlocker[] = {'r','e','s','t','a','r','t','M','e','s','s','a','g','e','U','n','l','o','c','k','e','r','M','a','c','\0'}; // "restartMessageUnlockerMac" #endif sb->FormatStringFromName(aUnlocker ? kRestartUnlocker : kRestartNoUnlocker, params, 2, getter_Copies(killMessage)); nsXPIDLString killTitle; sb->FormatStringFromName(NS_LITERAL_STRING("restartTitle").get(), params, 1, getter_Copies(killTitle)); if (!killMessage || !killTitle) return NS_ERROR_FAILURE; nsCOMPtr ps (do_GetService(NS_PROMPTSERVICE_CONTRACTID)); NS_ENSURE_TRUE(ps, NS_ERROR_FAILURE); PRUint32 flags = nsIPromptService::BUTTON_TITLE_OK * nsIPromptService::BUTTON_POS_0; if (aUnlocker) { flags = nsIPromptService::BUTTON_TITLE_CANCEL * nsIPromptService::BUTTON_POS_0 + nsIPromptService::BUTTON_TITLE_IS_STRING * nsIPromptService::BUTTON_POS_1 + nsIPromptService::BUTTON_POS_1_DEFAULT; } PRInt32 button; PRBool checkState; rv = ps->ConfirmEx(nsnull, killTitle, killMessage, flags, killTitle, nsnull, nsnull, nsnull, &checkState, &button); NS_ENSURE_SUCCESS_LOG(rv, rv); if (button == 1 && aUnlocker) { rv = aUnlocker->Unlock(nsIProfileUnlocker::FORCE_QUIT); if (NS_FAILED(rv)) return rv; return NS_LockProfilePath(aProfileDir, aProfileLocalDir, nsnull, aResult); } return NS_ERROR_ABORT; } } static nsresult ProfileMissingDialog(nsINativeAppSupport* aNative) { nsresult rv; ScopedXPCOMStartup xpcom; rv = xpcom.Initialize(); NS_ENSURE_SUCCESS(rv, rv); rv = xpcom.DoAutoreg(); rv |= xpcom.SetWindowCreator(aNative); NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE); { //extra scoping is needed so we release these components before xpcom shutdown nsCOMPtr sbs = mozilla::services::GetStringBundleService(); NS_ENSURE_TRUE(sbs, NS_ERROR_FAILURE); nsCOMPtr sb; sbs->CreateBundle(kProfileProperties, getter_AddRefs(sb)); NS_ENSURE_TRUE_LOG(sbs, NS_ERROR_FAILURE); NS_ConvertUTF8toUTF16 appName(gAppData->name); const PRUnichar* params[] = {appName.get(), appName.get()}; nsXPIDLString missingMessage; // profileMissing static const PRUnichar kMissing[] = {'p','r','o','f','i','l','e','M','i','s','s','i','n','g','\0'}; sb->FormatStringFromName(kMissing, params, 2, getter_Copies(missingMessage)); nsXPIDLString missingTitle; sb->FormatStringFromName(NS_LITERAL_STRING("profileMissingTitle").get(), params, 1, getter_Copies(missingTitle)); if (missingMessage && missingTitle) { nsCOMPtr ps (do_GetService(NS_PROMPTSERVICE_CONTRACTID)); NS_ENSURE_TRUE(ps, NS_ERROR_FAILURE); ps->Alert(nsnull, missingTitle, missingMessage); } return NS_ERROR_ABORT; } } static const char kProfileManagerURL[] = "chrome://mozapps/content/profile/profileSelection.xul"; static nsresult ShowProfileManager(nsIToolkitProfileService* aProfileSvc, nsINativeAppSupport* aNative) { nsresult rv; nsCOMPtr profD, profLD; PRUnichar* profileNamePtr; nsCAutoString profileName; { ScopedXPCOMStartup xpcom; rv = xpcom.Initialize(); NS_ENSURE_SUCCESS(rv, rv); rv = xpcom.DoAutoreg(); rv |= xpcom.RegisterProfileService(); rv |= xpcom.SetWindowCreator(aNative); NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE); #ifdef XP_MACOSX CommandLineServiceMac::SetupMacCommandLine(gRestartArgc, gRestartArgv, PR_TRUE); #endif #ifdef XP_WIN // we don't have to wait here because profile manager window will pump // and DDE message will be handled ProcessDDE(aNative, PR_FALSE); #endif { //extra scoping is needed so we release these components before xpcom shutdown nsCOMPtr windowWatcher (do_GetService(NS_WINDOWWATCHER_CONTRACTID)); nsCOMPtr ioParamBlock (do_CreateInstance(NS_DIALOGPARAMBLOCK_CONTRACTID)); nsCOMPtr dlgArray (do_CreateInstance(NS_ARRAY_CONTRACTID)); NS_ENSURE_TRUE(windowWatcher && ioParamBlock && dlgArray, NS_ERROR_FAILURE); ioParamBlock->SetObjects(dlgArray); nsCOMPtr appStartup (do_GetService(NS_APPSTARTUP_CONTRACTID)); NS_ENSURE_TRUE(appStartup, NS_ERROR_FAILURE); nsCOMPtr newWindow; rv = windowWatcher->OpenWindow(nsnull, kProfileManagerURL, "_blank", "centerscreen,chrome,modal,titlebar", ioParamBlock, getter_AddRefs(newWindow)); NS_ENSURE_SUCCESS_LOG(rv, rv); aProfileSvc->Flush(); PRInt32 dialogConfirmed; rv = ioParamBlock->GetInt(0, &dialogConfirmed); if (NS_FAILED(rv) || dialogConfirmed == 0) return NS_ERROR_ABORT; nsCOMPtr lock; rv = dlgArray->QueryElementAt(0, NS_GET_IID(nsIProfileLock), getter_AddRefs(lock)); NS_ENSURE_SUCCESS_LOG(rv, rv); rv = lock->GetDirectory(getter_AddRefs(profD)); NS_ENSURE_SUCCESS(rv, rv); rv = lock->GetLocalDirectory(getter_AddRefs(profLD)); NS_ENSURE_SUCCESS(rv, rv); rv = ioParamBlock->GetString(0, &profileNamePtr); NS_ENSURE_SUCCESS(rv, rv); CopyUTF16toUTF8(profileNamePtr, profileName); NS_Free(profileNamePtr); lock->Unlock(); } } SaveFileToEnv("XRE_PROFILE_PATH", profD); SaveFileToEnv("XRE_PROFILE_LOCAL_PATH", profLD); SaveWordToEnv("XRE_PROFILE_NAME", profileName); PRBool offline = PR_FALSE; aProfileSvc->GetStartOffline(&offline); if (offline) { SaveToEnv("XRE_START_OFFLINE=1"); } return LaunchChild(aNative); } static nsresult ImportProfiles(nsIToolkitProfileService* aPService, nsINativeAppSupport* aNative) { nsresult rv; SaveToEnv("XRE_IMPORT_PROFILES=1"); // try to import old-style profiles { // scope XPCOM ScopedXPCOMStartup xpcom; rv = xpcom.Initialize(); if (NS_SUCCEEDED(rv)) { xpcom.DoAutoreg(); xpcom.RegisterProfileService(); #ifdef XP_MACOSX CommandLineServiceMac::SetupMacCommandLine(gRestartArgc, gRestartArgv, PR_TRUE); #endif nsCOMPtr migrator (do_GetService(NS_PROFILEMIGRATOR_CONTRACTID)); if (migrator) { migrator->Import(); } } } aPService->Flush(); return LaunchChild(aNative); } // Pick a profile. We need to end up with a profile lock. // // 1) check for -profile // 2) check for -P // 3) check for -ProfileManager // 4) use the default profile, if there is one // 5) if there are *no* profiles, set up profile-migration // 6) display the profile-manager UI static PRBool gDoMigration = PR_FALSE; static nsresult SelectProfile(nsIProfileLock* *aResult, nsINativeAppSupport* aNative, PRBool* aStartOffline, nsACString* aProfileName) { nsresult rv; ArgResult ar; const char* arg; *aResult = nsnull; *aStartOffline = PR_FALSE; ar = CheckArg("offline", PR_TRUE); if (ar == ARG_BAD) { PR_fprintf(PR_STDERR, "Error: argument -offline is invalid when argument -osint is specified\n"); return NS_ERROR_FAILURE; } arg = PR_GetEnv("XRE_START_OFFLINE"); if ((arg && *arg) || ar) *aStartOffline = PR_TRUE; nsCOMPtr lf = GetFileFromEnv("XRE_PROFILE_PATH"); if (lf) { nsCOMPtr localDir = GetFileFromEnv("XRE_PROFILE_LOCAL_PATH"); if (!localDir) { localDir = lf; } arg = PR_GetEnv("XRE_PROFILE_NAME"); if (arg && *arg && aProfileName) aProfileName->Assign(nsDependentCString(arg)); // Clear out flags that we handled (or should have handled!) last startup. const char *dummy; CheckArg("p", PR_FALSE, &dummy); CheckArg("profile", PR_FALSE, &dummy); CheckArg("profilemanager"); return NS_LockProfilePath(lf, localDir, nsnull, aResult); } ar = CheckArg("migration", PR_TRUE); if (ar == ARG_BAD) { PR_fprintf(PR_STDERR, "Error: argument -migration is invalid when argument -osint is specified\n"); return NS_ERROR_FAILURE; } else if (ar == ARG_FOUND) { gDoMigration = PR_TRUE; } ar = CheckArg("profile", PR_TRUE, &arg); if (ar == ARG_BAD) { PR_fprintf(PR_STDERR, "Error: argument -profile requires a path\n"); return NS_ERROR_FAILURE; } if (ar) { nsCOMPtr lf; rv = XRE_GetFileFromPath(arg, getter_AddRefs(lf)); NS_ENSURE_SUCCESS(rv, rv); nsCOMPtr unlocker; // Check if the profile path exists and it's a directory. PRBool exists; lf->Exists(&exists); if (!exists) { rv = lf->Create(nsIFile::DIRECTORY_TYPE, 0644); NS_ENSURE_SUCCESS(rv, rv); } // If a profile path is specified directory on the command line, then // assume that the temp directory is the same as the given directory. rv = NS_LockProfilePath(lf, lf, getter_AddRefs(unlocker), aResult); if (NS_SUCCEEDED(rv)) return rv; return ProfileLockedDialog(lf, lf, unlocker, aNative, aResult); } nsCOMPtr profileSvc; rv = NS_NewToolkitProfileService(getter_AddRefs(profileSvc)); if (rv == NS_ERROR_FILE_ACCESS_DENIED) PR_fprintf(PR_STDERR, "Error: Access was denied while trying to open files in " \ "your profile directory.\n"); NS_ENSURE_SUCCESS(rv, rv); ar = CheckArg("createprofile", PR_TRUE, &arg); if (ar == ARG_BAD) { PR_fprintf(PR_STDERR, "Error: argument -createprofile requires a profile name\n"); return NS_ERROR_FAILURE; } if (ar) { nsCOMPtr profile; const char* delim = strchr(arg, ' '); if (delim) { nsCOMPtr lf; rv = NS_NewNativeLocalFile(nsDependentCString(delim + 1), PR_TRUE, getter_AddRefs(lf)); if (NS_FAILED(rv)) { PR_fprintf(PR_STDERR, "Error: profile path not valid.\n"); return rv; } // As with -profile, assume that the given path will be used for both the // main profile directory and the temp profile directory. rv = profileSvc->CreateProfile(lf, lf, nsDependentCSubstring(arg, delim), getter_AddRefs(profile)); } else { rv = profileSvc->CreateProfile(nsnull, nsnull, nsDependentCString(arg), getter_AddRefs(profile)); } // Some pathological arguments can make it this far if (NS_FAILED(rv)) { PR_fprintf(PR_STDERR, "Error creating profile.\n"); return rv; } rv = NS_ERROR_ABORT; profileSvc->Flush(); // XXXben need to ensure prefs.js exists here so the tinderboxes will // not go orange. nsCOMPtr prefsJSFile; profile->GetRootDir(getter_AddRefs(prefsJSFile)); prefsJSFile->AppendNative(NS_LITERAL_CSTRING("prefs.js")); nsCAutoString pathStr; prefsJSFile->GetNativePath(pathStr); PR_fprintf(PR_STDERR, "Success: created profile '%s' at '%s'\n", arg, pathStr.get()); PRBool exists; prefsJSFile->Exists(&exists); if (!exists) prefsJSFile->Create(nsIFile::NORMAL_FILE_TYPE, 0644); // XXXdarin perhaps 0600 would be better? return rv; } PRUint32 count; rv = profileSvc->GetProfileCount(&count); NS_ENSURE_SUCCESS(rv, rv); if (gAppData->flags & NS_XRE_ENABLE_PROFILE_MIGRATOR) { arg = PR_GetEnv("XRE_IMPORT_PROFILES"); if (!count && (!arg || !*arg)) { return ImportProfiles(profileSvc, aNative); } } ar = CheckArg("p", PR_FALSE, &arg); if (ar == ARG_BAD) { ar = CheckArg("osint"); if (ar == ARG_FOUND) { PR_fprintf(PR_STDERR, "Error: argument -p is invalid when argument -osint is specified\n"); return NS_ERROR_FAILURE; } return ShowProfileManager(profileSvc, aNative); } if (ar) { ar = CheckArg("osint"); if (ar == ARG_FOUND) { PR_fprintf(PR_STDERR, "Error: argument -p is invalid when argument -osint is specified\n"); return NS_ERROR_FAILURE; } nsCOMPtr profile; rv = profileSvc->GetProfileByName(nsDependentCString(arg), getter_AddRefs(profile)); if (NS_SUCCEEDED(rv)) { nsCOMPtr unlocker; rv = profile->Lock(nsnull, aResult); if (NS_SUCCEEDED(rv)) { if (aProfileName) aProfileName->Assign(nsDependentCString(arg)); return NS_OK; } nsCOMPtr profileDir; rv = profile->GetRootDir(getter_AddRefs(profileDir)); NS_ENSURE_SUCCESS(rv, rv); nsCOMPtr profileLocalDir; rv = profile->GetLocalDir(getter_AddRefs(profileLocalDir)); NS_ENSURE_SUCCESS(rv, rv); return ProfileLockedDialog(profileDir, profileLocalDir, unlocker, aNative, aResult); } return ShowProfileManager(profileSvc, aNative); } ar = CheckArg("profilemanager", PR_TRUE); if (ar == ARG_BAD) { PR_fprintf(PR_STDERR, "Error: argument -profilemanager is invalid when argument -osint is specified\n"); return NS_ERROR_FAILURE; } else if (ar == ARG_FOUND) { return ShowProfileManager(profileSvc, aNative); } if (!count) { gDoMigration = PR_TRUE; // create a default profile nsCOMPtr profile; nsresult rv = profileSvc->CreateProfile(nsnull, // choose a default dir for us nsnull, // choose a default dir for us NS_LITERAL_CSTRING("default"), getter_AddRefs(profile)); if (NS_SUCCEEDED(rv)) { profileSvc->Flush(); rv = profile->Lock(nsnull, aResult); if (NS_SUCCEEDED(rv)) { if (aProfileName) aProfileName->Assign(NS_LITERAL_CSTRING("default")); return NS_OK; } } } PRBool useDefault = PR_TRUE; if (count > 1) profileSvc->GetStartWithLastProfile(&useDefault); if (useDefault) { nsCOMPtr profile; // GetSelectedProfile will auto-select the only profile if there's just one profileSvc->GetSelectedProfile(getter_AddRefs(profile)); if (profile) { nsCOMPtr unlocker; rv = profile->Lock(getter_AddRefs(unlocker), aResult); if (NS_SUCCEEDED(rv)) { // Try to grab the profile name. if (aProfileName) { rv = profile->GetName(*aProfileName); if (NS_FAILED(rv)) aProfileName->Truncate(0); } return NS_OK; } nsCOMPtr profileDir; rv = profile->GetRootDir(getter_AddRefs(profileDir)); NS_ENSURE_SUCCESS(rv, rv); nsCOMPtr profileLocalDir; rv = profile->GetRootDir(getter_AddRefs(profileLocalDir)); NS_ENSURE_SUCCESS(rv, rv); return ProfileLockedDialog(profileDir, profileLocalDir, unlocker, aNative, aResult); } } return ShowProfileManager(profileSvc, aNative); } /** * Checks the compatibility.ini file to see if we have updated our application * or otherwise invalidated our caches. If the application has been updated, * we return PR_FALSE; otherwise, we return PR_TRUE. We also write the status * of the caches (valid/invalid) into the return param aCachesOK. The aCachesOK * is always invalid if the application has been updated. */ static PRBool CheckCompatibility(nsIFile* aProfileDir, const nsCString& aVersion, const nsCString& aOSABI, nsIFile* aXULRunnerDir, nsIFile* aAppDir, PRBool* aCachesOK) { *aCachesOK = false; nsCOMPtr file; aProfileDir->Clone(getter_AddRefs(file)); if (!file) return PR_FALSE; file->AppendNative(FILE_COMPATIBILITY_INFO); nsINIParser parser; nsCOMPtr localFile(do_QueryInterface(file)); nsresult rv = parser.Init(localFile); if (NS_FAILED(rv)) return PR_FALSE; nsCAutoString buf; rv = parser.GetString("Compatibility", "LastVersion", buf); if (NS_FAILED(rv) || !aVersion.Equals(buf)) return PR_FALSE; rv = parser.GetString("Compatibility", "LastOSABI", buf); if (NS_FAILED(rv) || !aOSABI.Equals(buf)) return PR_FALSE; rv = parser.GetString("Compatibility", "LastPlatformDir", buf); if (NS_FAILED(rv)) return PR_FALSE; nsCOMPtr lf; rv = NS_NewNativeLocalFile(buf, PR_FALSE, getter_AddRefs(lf)); if (NS_FAILED(rv)) return PR_FALSE; PRBool eq; rv = lf->Equals(aXULRunnerDir, &eq); if (NS_FAILED(rv) || !eq) return PR_FALSE; if (aAppDir) { rv = parser.GetString("Compatibility", "LastAppDir", buf); if (NS_FAILED(rv)) return PR_FALSE; rv = NS_NewNativeLocalFile(buf, PR_FALSE, getter_AddRefs(lf)); if (NS_FAILED(rv)) return PR_FALSE; rv = lf->Equals(aAppDir, &eq); if (NS_FAILED(rv) || !eq) return PR_FALSE; } rv = parser.GetString("Compatibility", "InvalidateCaches", buf); // If we see this flag, caches are invalid. *aCachesOK = (NS_FAILED(rv) || !buf.EqualsLiteral("1")); return PR_TRUE; } static void BuildVersion(nsCString &aBuf) { aBuf.Assign(gAppData->version); aBuf.Append('_'); aBuf.Append(gAppData->buildID); aBuf.Append('/'); aBuf.Append(gToolkitBuildID); } static void WriteVersion(nsIFile* aProfileDir, const nsCString& aVersion, const nsCString& aOSABI, nsIFile* aXULRunnerDir, nsIFile* aAppDir) { nsCOMPtr file; aProfileDir->Clone(getter_AddRefs(file)); if (!file) return; file->AppendNative(FILE_COMPATIBILITY_INFO); nsCOMPtr lf = do_QueryInterface(file); nsCAutoString platformDir; aXULRunnerDir->GetNativePath(platformDir); nsCAutoString appDir; if (aAppDir) aAppDir->GetNativePath(appDir); PRFileDesc *fd = nsnull; lf->OpenNSPRFileDesc(PR_WRONLY | PR_CREATE_FILE | PR_TRUNCATE, 0600, &fd); if (!fd) { NS_ERROR("could not create output stream"); return; } static const char kHeader[] = "[Compatibility]" NS_LINEBREAK "LastVersion="; PR_Write(fd, kHeader, sizeof(kHeader) - 1); PR_Write(fd, aVersion.get(), aVersion.Length()); static const char kOSABIHeader[] = NS_LINEBREAK "LastOSABI="; PR_Write(fd, kOSABIHeader, sizeof(kOSABIHeader) - 1); PR_Write(fd, aOSABI.get(), aOSABI.Length()); static const char kPlatformDirHeader[] = NS_LINEBREAK "LastPlatformDir="; PR_Write(fd, kPlatformDirHeader, sizeof(kPlatformDirHeader) - 1); PR_Write(fd, platformDir.get(), platformDir.Length()); static const char kAppDirHeader[] = NS_LINEBREAK "LastAppDir="; if (aAppDir) { PR_Write(fd, kAppDirHeader, sizeof(kAppDirHeader) - 1); PR_Write(fd, appDir.get(), appDir.Length()); } static const char kNL[] = NS_LINEBREAK; PR_Write(fd, kNL, sizeof(kNL) - 1); PR_Close(fd); } static void RemoveComponentRegistries(nsIFile* aProfileDir, nsIFile* aLocalProfileDir, PRBool aRemoveEMFiles) { nsCOMPtr file; aProfileDir->Clone(getter_AddRefs(file)); if (!file) return; file->AppendNative(NS_LITERAL_CSTRING("compreg.dat")); file->Remove(PR_FALSE); file->SetNativeLeafName(NS_LITERAL_CSTRING("xpti.dat")); file->Remove(PR_FALSE); if (aRemoveEMFiles) { file->SetNativeLeafName(NS_LITERAL_CSTRING("extensions.ini")); file->Remove(PR_FALSE); } aLocalProfileDir->Clone(getter_AddRefs(file)); if (!file) return; file->AppendNative(NS_LITERAL_CSTRING("XUL" PLATFORM_FASL_SUFFIX)); file->Remove(PR_FALSE); file->SetNativeLeafName(NS_LITERAL_CSTRING("XPC" PLATFORM_FASL_SUFFIX)); file->Remove(PR_FALSE); } // To support application initiated restart via nsIAppStartup.quit, we // need to save various environment variables, and then restore them // before re-launching the application. static struct { const char *name; char *value; } gSavedVars[] = { {"XUL_APP_FILE", nsnull} }; static void SaveStateForAppInitiatedRestart() { for (size_t i = 0; i < NS_ARRAY_LENGTH(gSavedVars); ++i) { const char *s = PR_GetEnv(gSavedVars[i].name); if (s) gSavedVars[i].value = PR_smprintf("%s=%s", gSavedVars[i].name, s); } } static void RestoreStateForAppInitiatedRestart() { for (size_t i = 0; i < NS_ARRAY_LENGTH(gSavedVars); ++i) { if (gSavedVars[i].value) PR_SetEnv(gSavedVars[i].value); } } #ifdef MOZ_CRASHREPORTER // When we first initialize the crash reporter we don't have a profile, // so we set the minidump path to $TEMP. Once we have a profile, // we set it to $PROFILE/minidumps, creating the directory // if needed. static void MakeOrSetMinidumpPath(nsIFile* profD) { nsCOMPtr dumpD; nsresult rv = profD->Clone(getter_AddRefs(dumpD)); if(dumpD) { PRBool fileExists; //XXX: do some more error checking here dumpD->Append(NS_LITERAL_STRING("minidumps")); rv = dumpD->Exists(&fileExists); if(!fileExists) { dumpD->Create(nsIFile::DIRECTORY_TYPE, 0700); } nsAutoString pathStr; if(NS_SUCCEEDED(dumpD->GetPath(pathStr))) CrashReporter::SetMinidumpPath(pathStr); } } #endif const nsXREAppData* gAppData = nsnull; #if defined(XP_OS2) // because we use early returns, we use a stack-based helper to un-set the OS2 FP handler class ScopedFPHandler { private: EXCEPTIONREGISTRATIONRECORD excpreg; public: ScopedFPHandler() { PR_OS2_SetFloatExcpHandler(&excpreg); } ~ScopedFPHandler() { PR_OS2_UnsetFloatExcpHandler(&excpreg); } }; #endif #ifdef MOZ_WIDGET_GTK2 #include "prlink.h" typedef void (*_g_set_application_name_fn)(const gchar *application_name); typedef void (*_gtk_window_set_auto_startup_notification_fn)(gboolean setting); static PRFuncPtr FindFunction(const char* aName) { PRLibrary *lib = nsnull; PRFuncPtr result = PR_FindFunctionSymbolAndLibrary(aName, &lib); // Since the library was already loaded, we can safely unload it here. if (lib) { PR_UnloadLibrary(lib); } return result; } static nsIWidget* GetMainWidget(nsIDOMWindow* aWindow) { // get the native window for this instance nsCOMPtr window(do_QueryInterface(aWindow)); NS_ENSURE_TRUE(window, nsnull); nsCOMPtr baseWindow (do_QueryInterface(window->GetDocShell())); NS_ENSURE_TRUE(baseWindow, nsnull); nsCOMPtr mainWidget; baseWindow->GetMainWidget(getter_AddRefs(mainWidget)); return mainWidget; } static nsGTKToolkit* GetGTKToolkit() { nsCOMPtr svc = do_GetService(NS_APPSHELLSERVICE_CONTRACTID); if (!svc) return nsnull; nsCOMPtr window; svc->GetHiddenDOMWindow(getter_AddRefs(window)); if (!window) return nsnull; nsIWidget* widget = GetMainWidget(window); if (!widget) return nsnull; nsIToolkit* toolkit = widget->GetToolkit(); if (!toolkit) return nsnull; return static_cast(toolkit); } static void MOZ_gdk_display_close(GdkDisplay *display) { // XXX wallpaper for bug 417163: don't close the Display if we're using the // Qt theme because we crash (in Qt code) when using jemalloc. PRBool theme_is_qt = PR_FALSE; GtkSettings* settings = gtk_settings_get_for_screen(gdk_display_get_default_screen(display)); gchar *theme_name; g_object_get(settings, "gtk-theme-name", &theme_name, NULL); if (theme_name) { theme_is_qt = strcmp(theme_name, "Qt") == 0; if (theme_is_qt) NS_WARNING("wallpaper bug 417163 for Qt theme"); g_free(theme_name); } // gdk_display_close was broken prior to gtk+-2.10.0. // (http://bugzilla.gnome.org/show_bug.cgi?id=85715) // gdk_display_manager_set_default_display (gdk_display_manager_get(), NULL) // was also broken. if (gtk_check_version(2,10,0) != NULL) { #ifdef MOZ_X11 // Version check failed - broken gdk_display_close. // // Let the gdk structures leak but at least close the Display, // assuming that gdk will not use it again. Display* dpy = GDK_DISPLAY_XDISPLAY(display); if (!theme_is_qt) XCloseDisplay(dpy); #else gdk_display_close(display); #endif /* MOZ_X11 */ } else { #if CLEANUP_MEMORY // Get a (new) Pango context that holds a reference to the fontmap that // GTK has been using. gdk_pango_context_get() must be called while GTK // has a default display. PangoContext *pangoContext = gdk_pango_context_get(); #endif PRBool buggyCairoShutdown = cairo_version() < CAIRO_VERSION_ENCODE(1, 4, 0); if (!buggyCairoShutdown) { // We should shut down GDK before we shut down libraries it depends on // like Pango and cairo. But if cairo shutdown is buggy, we should // shut down cairo first otherwise it may crash because of dangling // references to Display objects (see bug 469831). if (!theme_is_qt) gdk_display_close(display); } #if CLEANUP_MEMORY // This doesn't take a reference. PangoFontMap *fontmap = pango_context_get_font_map(pangoContext); // Do some shutdown of the fontmap, which releases the fonts, clearing a // bunch of circular references from the fontmap through the fonts back to // itself. The shutdown that this does is much less than what's done by // the fontmap's finalize, though. if (PANGO_IS_FC_FONT_MAP(fontmap)) pango_fc_font_map_shutdown(PANGO_FC_FONT_MAP(fontmap)); g_object_unref(pangoContext); // PangoCairo still holds a reference to the fontmap. // Now that we have finished with GTK and Pango, we could unref fontmap, // which would allow us to call FcFini, but removing what is really // Pango's ref feels a bit evil. Pango-1.22 will have support for // pango_cairo_font_map_set_default(NULL), which would release the // reference on the old fontmap. #if GTK_CHECK_VERSION(2,8,0) // cairo_debug_reset_static_data() is prototyped through cairo.h included // by gtk.h. #ifdef cairo_debug_reset_static_data #error "Looks like we're including Mozilla's cairo instead of system cairo" #endif cairo_debug_reset_static_data(); #endif // 2.8.0 #endif // CLEANUP_MEMORY if (buggyCairoShutdown) { if (!theme_is_qt) gdk_display_close(display); } } } #endif // MOZ_WIDGET_GTK2 /** * NSPR will search for the "nspr_use_zone_allocator" symbol throughout * the process and use it to determine whether the application defines its own * memory allocator or not. * * Since most applications (e.g. Firefox and Thunderbird) don't use any special * allocators and therefore don't define this symbol, NSPR must search the * entire process, which reduces startup performance. * * By defining the symbol here, we can avoid the wasted lookup and hopefully * improve startup performance. */ NS_VISIBILITY_DEFAULT PRBool nspr_use_zone_allocator = PR_FALSE; #ifdef MOZ_SPLASHSCREEN #define MOZ_SPLASHSCREEN_UPDATE(_i) do { if (splashScreen) splashScreen->Update(_i); } while(0) #else #define MOZ_SPLASHSCREEN_UPDATE(_i) do { } while(0) #endif #ifdef XP_WIN typedef BOOL (WINAPI* SetProcessDEPPolicyFunc)(DWORD dwFlags); #endif int XRE_main(int argc, char* argv[], const nsXREAppData* aAppData) { NS_TIME_FUNCTION; #ifdef MOZ_SPLASHSCREEN nsSplashScreen *splashScreen = nsnull; #endif nsresult rv; ArgResult ar; NS_TIMELINE_MARK("enter main"); #ifdef DEBUG if (PR_GetEnv("XRE_MAIN_BREAK")) NS_BREAK(); #endif SetupErrorHandling(argv[0]); #ifdef XP_UNIX const char *home = PR_GetEnv("HOME"); if (!home || !*home) { struct passwd *pw = getpwuid(geteuid()); if (!pw || !pw->pw_dir) { Output(PR_TRUE, "Could not determine HOME directory"); return 1; } SaveWordToEnv("HOME", nsDependentCString(pw->pw_dir)); } #endif #ifdef MOZ_ACCESSIBILITY_ATK // Reset GTK_MODULES, strip atk-bridge if exists // Mozilla will load libatk-bridge.so later if necessary const char* gtkModules = PR_GetEnv("GTK_MODULES"); if (gtkModules && *gtkModules) { nsCString gtkModulesStr(gtkModules); gtkModulesStr.ReplaceSubstring("atk-bridge", ""); char* expr = PR_smprintf("GTK_MODULES=%s", gtkModulesStr.get()); if (expr) PR_SetEnv(expr); // We intentionally leak |expr| here since it is required by PR_SetEnv. } // Suppress atk-bridge init at startup, it works after GNOME 2.24.2 PR_SetEnv("NO_AT_BRIDGE=1"); #endif gArgc = argc; gArgv = argv; NS_ENSURE_TRUE(aAppData, 2); #ifdef XP_MACOSX // The xulrunner stub executable tricks CFBundleGetMainBundle on // purpose into lying about the main bundle path. It will set // XRE_BINARY_PATH to inform us of our real location. gBinaryPath = getenv("XRE_BINARY_PATH"); if (gBinaryPath && !*gBinaryPath) gBinaryPath = nsnull; #endif // Check for application.ini overrides const char* override = nsnull; ar = CheckArg("override", PR_TRUE, &override); if (ar == ARG_BAD) { Output(PR_TRUE, "Incorrect number of arguments passed to -override"); return 1; } else if (ar == ARG_FOUND) { nsCOMPtr overrideLF; rv = XRE_GetFileFromPath(override, getter_AddRefs(overrideLF)); if (NS_FAILED(rv)) { Output(PR_TRUE, "Error: unrecognized override.ini path.\n"); return 1; } nsXREAppData* overrideAppData = const_cast(aAppData); rv = XRE_ParseAppData(overrideLF, overrideAppData); if (NS_FAILED(rv)) { Output(PR_TRUE, "Couldn't read override.ini"); return 1; } } ScopedAppData appData(aAppData); gAppData = &appData; // Check sanity and correctness of app data. if (!appData.name) { Output(PR_TRUE, "Error: App:Name not specified in application.ini\n"); return 1; } if (!appData.buildID) { Output(PR_TRUE, "Error: App:BuildID not specified in application.ini\n"); return 1; } #ifdef MOZ_SPLASHSCREEN // check to see if we need to do a splash screen PRBool wantsSplash = PR_TRUE; PRBool isNoSplash = (CheckArg("nosplash", PR_FALSE, NULL, PR_FALSE) == ARG_FOUND); isNoSplash |= (PR_GetEnv("NO_SPLASH") != 0); PRBool isNoRemote = (CheckArg("no-remote", PR_FALSE, NULL, PR_FALSE) == ARG_FOUND); #ifdef WINCE // synchronize startup; if it looks like we're going to have to // wait, then open up a splash screen WindowsMutex winStartupMutex(L"FirefoxStartupMutex"); // try to lock the mutex, but only wait 100ms to do so PRBool needsMutexLock = ! winStartupMutex.Lock(100); // If we failed to lock the mutex quickly, then we'll want // a splash screen for sure. // // If we did manage to lock it, then we'll only want one // a splash screen if there is no existing message window; // that is, if we are the first instance of the app. if (!needsMutexLock && !isNoRemote) { // check to see if there's a remote firefox up static PRUnichar classNameBuffer[128]; _snwprintf(classNameBuffer, sizeof(classNameBuffer) / sizeof(PRUnichar), L"%S%s", gAppData->name, L"MessageWindow"); HANDLE h = FindWindowW(classNameBuffer, 0); if (h) { // Someone else has the window, and we were able to grab the mutex, // meaning the other instance ahs presumably already finished starting // up by now. So no need for a splash screen. wantsSplash = PR_FALSE; CloseHandle(h); } else { // We couldn't find another window, and we were able to lock the mutex; // we're likely the first instance starting up, so make sure a splash // screen gets thrown up. wantsSplash = PR_TRUE; } } #endif //WINCE if (wantsSplash && !isNoSplash) splashScreen = nsSplashScreen::GetOrCreate(); if (splashScreen) splashScreen->Open(); #ifdef WINCE // Now that the splash screen is open, wait indefinitely // for the startup mutex on this thread if we need to. if (needsMutexLock) winStartupMutex.Lock(); #endif //WINCE #endif //MOZ_SPLASHSCREEN ScopedLogging log; if (!appData.xreDirectory) { nsCOMPtr lf; rv = XRE_GetBinaryPath(gArgv[0], getter_AddRefs(lf)); if (NS_FAILED(rv)) return 2; nsCOMPtr greDir; rv = lf->GetParent(getter_AddRefs(greDir)); if (NS_FAILED(rv)) return 2; rv = CallQueryInterface(greDir, &appData.xreDirectory); if (NS_FAILED(rv)) return 2; } if (appData.size > offsetof(nsXREAppData, minVersion)) { if (!appData.minVersion) { Output(PR_TRUE, "Error: Gecko:MinVersion not specified in application.ini\n"); return 1; } if (!appData.maxVersion) { // If no maxVersion is specified, we assume the app is only compatible // with the initial preview release. Do not increment this number ever! SetAllocatedString(appData.maxVersion, "1.*"); } if (NS_CompareVersions(appData.minVersion, gToolkitVersion) > 0 || NS_CompareVersions(appData.maxVersion, gToolkitVersion) < 0) { Output(PR_TRUE, "Error: Platform version '%s' is not compatible with\n" "minVersion >= %s\nmaxVersion <= %s\n", gToolkitVersion, appData.minVersion, appData.maxVersion); return 1; } } nsXREDirProvider dirProvider; rv = dirProvider.Initialize(gAppData->directory, gAppData->xreDirectory); if (NS_FAILED(rv)) return 1; #ifdef MOZ_CRASHREPORTER const char* crashreporterEnv = PR_GetEnv("MOZ_CRASHREPORTER"); if (crashreporterEnv && *crashreporterEnv) { appData.flags |= NS_XRE_ENABLE_CRASH_REPORTER; } if ((appData.flags & NS_XRE_ENABLE_CRASH_REPORTER) && NS_SUCCEEDED( CrashReporter::SetExceptionHandler(appData.xreDirectory))) { if (appData.crashReporterURL) CrashReporter::SetServerURL(nsDependentCString(appData.crashReporterURL)); // pass some basic info from the app data if (appData.vendor) CrashReporter::AnnotateCrashReport(NS_LITERAL_CSTRING("Vendor"), nsDependentCString(appData.vendor)); if (appData.name) CrashReporter::AnnotateCrashReport(NS_LITERAL_CSTRING("ProductName"), nsDependentCString(appData.name)); if (appData.version) CrashReporter::AnnotateCrashReport(NS_LITERAL_CSTRING("Version"), nsDependentCString(appData.version)); if (appData.buildID) CrashReporter::AnnotateCrashReport(NS_LITERAL_CSTRING("BuildID"), nsDependentCString(appData.buildID)); CrashReporter::SetRestartArgs(argc, argv); // annotate other data (user id etc) nsCOMPtr userAppDataDir; if (NS_SUCCEEDED(dirProvider.GetUserAppDataDirectory( getter_AddRefs(userAppDataDir)))) { CrashReporter::SetupExtraData(userAppDataDir, nsDependentCString(appData.buildID)); // see if we have a crashreporter-override.ini in the application directory nsCOMPtr overrideini; PRBool exists; static char overrideEnv[MAXPATHLEN]; if (NS_SUCCEEDED(dirProvider.GetAppDir()->Clone(getter_AddRefs(overrideini))) && NS_SUCCEEDED(overrideini->AppendNative(NS_LITERAL_CSTRING("crashreporter-override.ini"))) && NS_SUCCEEDED(overrideini->Exists(&exists)) && exists) { #ifdef XP_WIN nsAutoString overridePathW; overrideini->GetPath(overridePathW); NS_ConvertUTF16toUTF8 overridePath(overridePathW); #else nsCAutoString overridePath; overrideini->GetNativePath(overridePath); #endif sprintf(overrideEnv, "MOZ_CRASHREPORTER_STRINGS_OVERRIDE=%s", overridePath.get()); PR_SetEnv(overrideEnv); } } } #endif #ifdef XP_MACOSX if (PR_GetEnv("MOZ_LAUNCHED_CHILD")) { // This is needed, on relaunch, to force the OS to use the "Cocoa Dock // API". Otherwise the call to ReceiveNextEvent() below will make it // use the "Carbon Dock API". For more info see bmo bug 377166. EnsureUseCocoaDockAPI(); // When the app relaunches, the original process exits. This causes // the dock tile to stop bouncing, lose the "running" triangle, and // if the tile does not permanently reside in the Dock, even disappear. // This can be confusing to the user, who is expecting the app to launch. // Calling ReceiveNextEvent without requesting any event is enough to // cause a dock tile for the child process to appear. const EventTypeSpec kFakeEventList[] = { { INT_MAX, INT_MAX } }; EventRef event; ::ReceiveNextEvent(GetEventTypeCount(kFakeEventList), kFakeEventList, kEventDurationNoWait, PR_FALSE, &event); } if (CheckArg("foreground")) { // The original process communicates that it was in the foreground by // adding this argument. This new process, which is taking over for // the old one, should make itself the active application. ProcessSerialNumber psn; if (::GetCurrentProcess(&psn) == noErr) ::SetFrontProcess(&psn); } #endif SaveToEnv("MOZ_LAUNCHED_CHILD="); gRestartArgc = gArgc; gRestartArgv = (char**) malloc(sizeof(char*) * (gArgc + 1 + (override ? 2 : 0))); if (!gRestartArgv) return 1; int i; for (i = 0; i < gArgc; ++i) { gRestartArgv[i] = gArgv[i]; } // Add the -override argument back (it is removed automatically be CheckArg) if there is one if (override) { gRestartArgv[gRestartArgc++] = const_cast("-override"); gRestartArgv[gRestartArgc++] = const_cast(override); } gRestartArgv[gRestartArgc] = nsnull; #if defined(XP_OS2) PRBool StartOS2App(int aArgc, char **aArgv); if (!StartOS2App(gArgc, gArgv)) return 1; ScopedFPHandler handler; #endif /* XP_OS2 */ ar = CheckArg("safe-mode", PR_TRUE); if (ar == ARG_BAD) { PR_fprintf(PR_STDERR, "Error: argument -safe-mode is invalid when argument -osint is specified\n"); return 1; } else if (ar == ARG_FOUND) { gSafeMode = PR_TRUE; } #ifdef XP_MACOSX if (GetCurrentEventKeyModifiers() & optionKey) gSafeMode = PR_TRUE; #endif // Handle -no-remote command line argument. Setup the environment to // better accommodate other components and various restart scenarios. ar = CheckArg("no-remote", PR_TRUE); if (ar == ARG_BAD) { PR_fprintf(PR_STDERR, "Error: argument -a requires an application name\n"); return 1; } else if (ar == ARG_FOUND) { SaveToEnv("MOZ_NO_REMOTE=1"); } // Handle -help and -version command line arguments. // They should return quickly, so we deal with them here. if (CheckArg("h") || CheckArg("help") || CheckArg("?")) { DumpHelp(); return 0; } if (CheckArg("v") || CheckArg("version")) { DumpVersion(); return 0; } #ifdef NS_TRACE_MALLOC gArgc = argc = NS_TraceMallocStartupArgs(gArgc, gArgv); #endif MOZ_SPLASHSCREEN_UPDATE(20); rv = XRE_InitCommandLine(gArgc, gArgv); NS_ENSURE_SUCCESS(rv, 1); { // Check for -register, which registers chrome and then exits immediately. ar = CheckArg("register", PR_TRUE); if (ar == ARG_BAD) { PR_fprintf(PR_STDERR, "Error: argument -register is invalid when argument -osint is specified\n"); return 1; } else if (ar == ARG_FOUND) { ScopedXPCOMStartup xpcom; rv = xpcom.Initialize(); NS_ENSURE_SUCCESS(rv, 1); { nsCOMPtr chromeReg = mozilla::services::GetChromeRegistryService(); NS_ENSURE_TRUE(chromeReg, 1); chromeReg->CheckForNewChrome(); } return 0; } #if defined(MOZ_WIDGET_GTK2) || defined(MOZ_ENABLE_XREMOTE) // Stash DESKTOP_STARTUP_ID in malloc'ed memory because gtk_init will clear it. #define HAVE_DESKTOP_STARTUP_ID const char* desktopStartupIDEnv = PR_GetEnv("DESKTOP_STARTUP_ID"); nsCAutoString desktopStartupID; if (desktopStartupIDEnv) { desktopStartupID.Assign(desktopStartupIDEnv); } #endif #if defined(MOZ_WIDGET_QT) const char* qgraphicssystemARG = NULL; ar = CheckArg("graphicssystem", PR_TRUE, &qgraphicssystemARG, PR_FALSE); if (ar == ARG_FOUND) PR_SetEnv(PR_smprintf("MOZ_QT_GRAPHICSSYSTEM=%s", qgraphicssystemARG)); QApplication app(gArgc, gArgv); #endif #if defined(MOZ_WIDGET_GTK2) #ifdef MOZ_MEMORY // Disable the slice allocator, since jemalloc already uses similar layout // algorithms, and using a sub-allocator tends to increase fragmentation. // This must be done before g_thread_init() is called. g_slice_set_config(G_SLICE_CONFIG_ALWAYS_MALLOC, 1); #endif g_thread_init(NULL); // setup for private colormap. Ideally we'd like to do this // in nsAppShell::Create, but we need to get in before gtk // has been initialized to make sure everything is running // consistently. if (CheckArg("install")) gdk_rgb_set_install(TRUE); // Initialize GTK here for splash. // Open the display ourselves instead of using gtk_init, so that we can // close it without fear that one day gtk might clean up the display it // opens. if (!gtk_parse_args(&gArgc, &gArgv)) return 1; // display_name is owned by gdk. const char *display_name = gdk_get_display_arg_name(); if (display_name) { SaveWordToEnv("DISPLAY", nsDependentCString(display_name)); } else { display_name = PR_GetEnv("DISPLAY"); if (!display_name) { PR_fprintf(PR_STDERR, "Error: no display specified\n"); return 1; } } #endif /* MOZ_WIDGET_GTK2 */ #ifdef MOZ_ENABLE_XREMOTE // handle -remote now that xpcom is fired up const char* xremotearg; ar = CheckArg("remote", PR_TRUE, &xremotearg); if (ar == ARG_BAD) { PR_fprintf(PR_STDERR, "Error: -remote requires an argument\n"); return 1; } const char* desktopStartupIDPtr = desktopStartupID.IsEmpty() ? nsnull : desktopStartupID.get(); if (ar) { return HandleRemoteArgument(xremotearg, desktopStartupIDPtr); } if (!PR_GetEnv("MOZ_NO_REMOTE")) { // Try to remote the entire command line. If this fails, start up normally. RemoteResult rr = RemoteCommandLine(desktopStartupIDPtr); if (rr == REMOTE_FOUND) return 0; else if (rr == REMOTE_ARG_BAD) return 1; } #endif #if defined(MOZ_WIDGET_GTK2) GdkDisplay* display = nsnull; display = gdk_display_open(display_name); if (!display) { PR_fprintf(PR_STDERR, "Error: cannot open display: %s\n", display_name); return 1; } gdk_display_manager_set_default_display (gdk_display_manager_get(), display); // g_set_application_name () is only defined in glib2.2 and higher. _g_set_application_name_fn _g_set_application_name = (_g_set_application_name_fn)FindFunction("g_set_application_name"); if (_g_set_application_name) { _g_set_application_name(gAppData->name); } _gtk_window_set_auto_startup_notification_fn _gtk_window_set_auto_startup_notification = (_gtk_window_set_auto_startup_notification_fn)FindFunction("gtk_window_set_auto_startup_notification"); if (_gtk_window_set_auto_startup_notification) { _gtk_window_set_auto_startup_notification(PR_FALSE); } gtk_widget_set_default_colormap(gdk_rgb_get_colormap()); #endif /* MOZ_WIDGET_GTK2 */ #ifdef MOZ_X11 // Do this after initializing GDK, or GDK will install its own handler. InstallX11ErrorHandler(); #endif // Call the code to install our handler #ifdef MOZ_JPROF setupProfilingStuff(); #endif // Try to allocate "native app support." nsCOMPtr nativeApp; rv = NS_CreateNativeAppSupport(getter_AddRefs(nativeApp)); if (NS_FAILED(rv)) return 1; PRBool canRun = PR_FALSE; rv = nativeApp->Start(&canRun); if (NS_FAILED(rv) || !canRun) { return 1; } #if defined(MOZ_UPDATER) // Check for and process any available updates nsCOMPtr updRoot; PRBool persistent; rv = dirProvider.GetFile(XRE_UPDATE_ROOT_DIR, &persistent, getter_AddRefs(updRoot)); // XRE_UPDATE_ROOT_DIR may fail. Fallback to appDir if failed if (NS_FAILED(rv)) updRoot = dirProvider.GetAppDir(); ProcessUpdates(dirProvider.GetGREDir(), dirProvider.GetAppDir(), updRoot, gRestartArgc, gRestartArgv, appData.version); #endif nsCOMPtr profileLock; PRBool startOffline = PR_FALSE; nsCAutoString profileName; rv = SelectProfile(getter_AddRefs(profileLock), nativeApp, &startOffline, &profileName); if (rv == NS_ERROR_LAUNCHED_CHILD_PROCESS || rv == NS_ERROR_ABORT) return 0; if (NS_FAILED(rv)) { // We failed to choose or create profile - notify user and quit ProfileMissingDialog(nativeApp); return 1; } nsCOMPtr profD; rv = profileLock->GetDirectory(getter_AddRefs(profD)); NS_ENSURE_SUCCESS(rv, 1); nsCOMPtr profLD; rv = profileLock->GetLocalDirectory(getter_AddRefs(profLD)); NS_ENSURE_SUCCESS(rv, 1); rv = dirProvider.SetProfile(profD, profLD); NS_ENSURE_SUCCESS(rv, 1); #if defined(WINCE) && defined(MOZ_SPLASHSCREEN) // give up the mutex, let other app startups happen winStartupMutex.Unlock(); #endif //////////////////////// NOW WE HAVE A PROFILE //////////////////////// #ifdef MOZ_CRASHREPORTER if (appData.flags & NS_XRE_ENABLE_CRASH_REPORTER) MakeOrSetMinidumpPath(profD); #endif PRBool upgraded = PR_FALSE; nsCAutoString version; BuildVersion(version); #ifdef TARGET_OS_ABI NS_NAMED_LITERAL_CSTRING(osABI, TARGET_OS_ABI); #else // No TARGET_XPCOM_ABI, but at least the OS is known NS_NAMED_LITERAL_CSTRING(osABI, OS_TARGET "_UNKNOWN"); #endif // Check for version compatibility with the last version of the app this // profile was started with. The format of the version stamp is defined // by the BuildVersion function. // Also check to see if something has happened to invalidate our // fastload caches, like an extension upgrade or installation. PRBool cachesOK; PRBool versionOK = CheckCompatibility(profD, version, osABI, dirProvider.GetGREDir(), gAppData->directory, &cachesOK); // Every time a profile is loaded by a build with a different version, // it updates the compatibility.ini file saying what version last wrote // the compreg.dat. On subsequent launches if the version matches, // there is no need for re-registration. If the user loads the same // profile in different builds the component registry must be // re-generated to prevent mysterious component loading failures. // if (gSafeMode) { RemoveComponentRegistries(profD, profLD, PR_FALSE); WriteVersion(profD, NS_LITERAL_CSTRING("Safe Mode"), osABI, dirProvider.GetGREDir(), gAppData->directory); } else if (versionOK) { if (!cachesOK) { // Remove compreg.dat and xpti.dat, forcing component re-registration. // The new list of additional components directories is derived from // information in "extensions.ini". RemoveComponentRegistries(profD, profLD, PR_FALSE); // Rewrite compatibility.ini to remove the flag WriteVersion(profD, version, osABI, dirProvider.GetGREDir(), gAppData->directory); } // Nothing need be done for the normal startup case. } else { // Remove compreg.dat and xpti.dat, forcing component re-registration // with the default set of components (this disables any potentially // troublesome incompatible XPCOM components). RemoveComponentRegistries(profD, profLD, PR_TRUE); // Tell the Extension Manager it should check for incompatible // Extensions and re-write the "extensions.ini" file with a list of // directories for compatible extensions upgraded = PR_TRUE; // Write out version WriteVersion(profD, version, osABI, dirProvider.GetGREDir(), gAppData->directory); } PRBool needsRestart = PR_FALSE; PRBool appInitiatedRestart = PR_FALSE; MOZ_SPLASHSCREEN_UPDATE(30); NS_TIME_FUNCTION_MARK("Next: ScopedXPCOMStartup"); NS_TIME_FUNCTION_MARK("ScopedXPCOMStartup"); // Allows the user to forcefully bypass the restart process at their // own risk. Useful for debugging or for tinderboxes where child // processes can be problematic. { // Start the real application ScopedXPCOMStartup xpcom; rv = xpcom.Initialize(); NS_TIME_FUNCTION_MARK("ScopedXPCOMStartup: Initialize"); NS_ENSURE_SUCCESS(rv, 1); rv = xpcom.DoAutoreg(); NS_TIME_FUNCTION_MARK("ScopedXPCOMStartup: DoAutoreg"); #ifdef NS_FUNCTION_TIMER // initialize some common services, so we don't pay the cost for these at odd times later on; // SetWindowCreator -> ChromeRegistry -> IOService -> SocketTransportService -> (nspr wspm init), Prefs { nsCOMPtr comp; comp = do_GetService("@mozilla.org/preferences-service;1"); NS_TIME_FUNCTION_MARK("Pref Service"); comp = do_GetService("@mozilla.org/network/socket-transport-service;1"); NS_TIME_FUNCTION_MARK("Socket Transport Service"); comp = do_GetService("@mozilla.org/network/dns-service;1"); NS_TIME_FUNCTION_MARK("DNS Service"); comp = do_GetService("@mozilla.org/network/io-service;1"); NS_TIME_FUNCTION_MARK("IO Service"); comp = do_GetService("@mozilla.org/chrome/chrome-registry;1"); NS_TIME_FUNCTION_MARK("Chrome Registry Service"); comp = do_GetService("@mozilla.org/focus-event-suppressor-service;1"); NS_TIME_FUNCTION_MARK("Focus Event Suppressor Service"); } #endif rv |= xpcom.RegisterProfileService(); NS_TIME_FUNCTION_MARK("ScopedXPCOMStartup: RegisterProfileService"); rv |= xpcom.SetWindowCreator(nativeApp); NS_TIME_FUNCTION_MARK("ScopedXPCOMStartup: SetWindowCreator"); NS_ENSURE_SUCCESS(rv, 1); NS_TIME_FUNCTION_MARK("ScopedXPCOMStartup: Done"); #ifdef MOZ_CRASHREPORTER // tell the crash reporter to also send the release channel nsCOMPtr prefs = do_GetService("@mozilla.org/preferences-service;1", &rv); if (NS_SUCCEEDED(rv)) { nsCOMPtr defaultPrefBranch; rv = prefs->GetDefaultBranch(nsnull, getter_AddRefs(defaultPrefBranch)); if (NS_SUCCEEDED(rv)) { nsXPIDLCString sval; rv = defaultPrefBranch->GetCharPref("app.update.channel", getter_Copies(sval)); if (NS_SUCCEEDED(rv)) { CrashReporter::AnnotateCrashReport(NS_LITERAL_CSTRING("ReleaseChannel"), sval); } } } #endif NS_TIME_FUNCTION_MARK("Next: AppStartup"); { if (startOffline) { nsCOMPtr io (do_GetService("@mozilla.org/network/io-service;1")); NS_ENSURE_TRUE(io, 1); io->SetManageOfflineStatus(PR_FALSE); io->SetOffline(PR_TRUE); } { NS_TIMELINE_ENTER("startupNotifier"); nsCOMPtr startupNotifier (do_CreateInstance(NS_APPSTARTUPNOTIFIER_CONTRACTID, &rv)); NS_ENSURE_SUCCESS(rv, 1); startupNotifier->Observe(nsnull, APPSTARTUP_TOPIC, nsnull); NS_TIMELINE_LEAVE("startupNotifier"); } NS_TIME_FUNCTION_MARK("Finished startupNotifier"); nsCOMPtr appStartup (do_GetService(NS_APPSTARTUP_CONTRACTID)); NS_ENSURE_TRUE(appStartup, 1); NS_TIME_FUNCTION_MARK("Created AppStartup"); if (gDoMigration) { nsCOMPtr file; dirProvider.GetAppDir()->Clone(getter_AddRefs(file)); file->AppendNative(NS_LITERAL_CSTRING("override.ini")); nsINIParser parser; nsCOMPtr localFile(do_QueryInterface(file)); nsresult rv = parser.Init(localFile); if (NS_SUCCEEDED(rv)) { nsCAutoString buf; rv = parser.GetString("XRE", "EnableProfileMigrator", buf); if (NS_SUCCEEDED(rv)) { if (buf[0] == '0' || buf[0] == 'f' || buf[0] == 'F') { gDoMigration = PR_FALSE; } } } } // Profile Migration if (gAppData->flags & NS_XRE_ENABLE_PROFILE_MIGRATOR && gDoMigration) { gDoMigration = PR_FALSE; nsCOMPtr pm (do_CreateInstance(NS_PROFILEMIGRATOR_CONTRACTID)); if (pm) pm->Migrate(&dirProvider); } NS_TIME_FUNCTION_MARK("Profile migration"); dirProvider.DoStartup(); NS_TIME_FUNCTION_MARK("dirProvider.DoStartup() (profile-after-change)"); PRBool shuttingDown = PR_FALSE; appStartup->GetShuttingDown(&shuttingDown); nsCOMPtr cmdLine; nsCOMPtr workingDir; rv = NS_GetSpecialDirectory(NS_OS_CURRENT_WORKING_DIR, getter_AddRefs(workingDir)); NS_ENSURE_SUCCESS(rv, 1); if (!shuttingDown) { cmdLine = do_CreateInstance("@mozilla.org/toolkit/command-line;1"); NS_ENSURE_TRUE(cmdLine, 1); rv = cmdLine->Init(gArgc, gArgv, workingDir, nsICommandLine::STATE_INITIAL_LAUNCH); NS_ENSURE_SUCCESS(rv, 1); /* Special-case services that need early access to the command line. */ nsCOMPtr obsService = mozilla::services::GetObserverService(); if (obsService) { obsService->NotifyObservers(cmdLine, "command-line-startup", nsnull); } NS_TIME_FUNCTION_MARK("Early command line init"); NS_TIME_FUNCTION_MARK("Next: prepare for Run"); if (!upgraded) appStartup->GetNeedsRestart(&needsRestart); // We want to restart no more than 2 times. The first restart, // NO_EM_RESTART == "0" , and the second time, "1". char* noEMRestart = PR_GetEnv("NO_EM_RESTART"); if (noEMRestart && *noEMRestart && *noEMRestart == '1') { if (upgraded || needsRestart) { NS_WARNING("EM tried to force us to restart twice! Forcefully preventing that."); } needsRestart = upgraded = PR_FALSE; } } if (!upgraded && !needsRestart) { SaveStateForAppInitiatedRestart(); // clear out any environment variables which may have been set // during the relaunch process now that we know we won't be relaunching. SaveToEnv("XRE_PROFILE_PATH="); SaveToEnv("XRE_PROFILE_LOCAL_PATH="); SaveToEnv("XRE_PROFILE_NAME="); SaveToEnv("XRE_START_OFFLINE="); SaveToEnv("XRE_IMPORT_PROFILES="); SaveToEnv("NO_EM_RESTART="); SaveToEnv("XUL_APP_FILE="); SaveToEnv("XRE_BINARY_PATH="); NS_TIME_FUNCTION_MARK("env munging"); if (!shuttingDown) { NS_TIME_FUNCTION_MARK("Next: CreateHiddenWindow"); NS_TIMELINE_ENTER("appStartup->CreateHiddenWindow"); rv = appStartup->CreateHiddenWindow(); NS_TIMELINE_LEAVE("appStartup->CreateHiddenWindow"); NS_ENSURE_SUCCESS(rv, 1); MOZ_SPLASHSCREEN_UPDATE(50); #if defined(HAVE_DESKTOP_STARTUP_ID) && defined(MOZ_WIDGET_GTK2) nsRefPtr toolkit = GetGTKToolkit(); if (toolkit && !desktopStartupID.IsEmpty()) { toolkit->SetDesktopStartupID(desktopStartupID); } #endif #ifdef XP_MACOSX // we re-initialize the command-line service and do appleevents munging // after we are sure that we're not restarting cmdLine = do_CreateInstance("@mozilla.org/toolkit/command-line;1"); NS_ENSURE_TRUE(cmdLine, 1); CommandLineServiceMac::SetupMacCommandLine(gArgc, gArgv, PR_FALSE); rv = cmdLine->Init(gArgc, gArgv, workingDir, nsICommandLine::STATE_INITIAL_LAUNCH); NS_ENSURE_SUCCESS(rv, 1); // Set up ability to respond to system (Apple) events. SetupMacApplicationDelegate(); #endif MOZ_SPLASHSCREEN_UPDATE(70); nsCOMPtr obsService = mozilla::services::GetObserverService(); if (obsService) obsService->NotifyObservers(nsnull, "final-ui-startup", nsnull); NS_TIME_FUNCTION_MARK("final-ui-startup done"); appStartup->GetShuttingDown(&shuttingDown); } if (!shuttingDown) { rv = cmdLine->Run(); NS_ENSURE_SUCCESS_LOG(rv, 1); appStartup->GetShuttingDown(&shuttingDown); } #ifdef MOZ_ENABLE_XREMOTE nsCOMPtr remoteService; #endif /* MOZ_ENABLE_XREMOTE */ if (!shuttingDown) { #ifdef MOZ_ENABLE_XREMOTE // if we have X remote support, start listening for requests on the // proxy window. remoteService = do_GetService("@mozilla.org/toolkit/remote-service;1"); if (remoteService) remoteService->Startup(gAppData->name, PromiseFlatCString(profileName).get()); #endif /* MOZ_ENABLE_XREMOTE */ nativeApp->Enable(); } NS_TIME_FUNCTION_MARK("Next: Run"); NS_TIME_FUNCTION_MARK("appStartup->Run"); MOZ_SPLASHSCREEN_UPDATE(90); { NS_TIMELINE_ENTER("appStartup->Run"); rv = appStartup->Run(); NS_TIMELINE_LEAVE("appStartup->Run"); if (NS_FAILED(rv)) { NS_ERROR("failed to run appstartup"); gLogConsoleErrors = PR_TRUE; } } NS_TIME_FUNCTION_MARK("Next: Finish"); NS_TIME_FUNCTION_MARK("appStartup->Run done"); // Check for an application initiated restart. This is one that // corresponds to nsIAppStartup.quit(eRestart) if (rv == NS_SUCCESS_RESTART_APP) { needsRestart = PR_TRUE; appInitiatedRestart = PR_TRUE; } if (!shuttingDown) { #ifdef MOZ_ENABLE_XREMOTE // shut down the x remote proxy window if (remoteService) remoteService->Shutdown(); #endif /* MOZ_ENABLE_XREMOTE */ } #ifdef MOZ_TIMELINE // Make sure we print this out even if timeline is runtime disabled if (NS_FAILED(NS_TIMELINE_LEAVE("main1"))) NS_TimelineForceMark("...main1"); #endif } else { // Upgrade condition (build id changes), but the restart hint was // not set by the Extension Manager. This is because the compatibility // resolution for Extensions is different than for the component // registry - major milestone vs. build id. needsRestart = PR_TRUE; #ifdef XP_WIN ProcessDDE(nativeApp, PR_TRUE); #endif #ifdef XP_MACOSX CommandLineServiceMac::SetupMacCommandLine(gRestartArgc, gRestartArgv, PR_TRUE); #endif } } } // unlock the profile after ScopedXPCOMStartup object (xpcom) // has gone out of scope. see bug #386739 for more details profileLock->Unlock(); // Restart the app after XPCOM has been shut down cleanly. if (needsRestart) { MOZ_SPLASHSCREEN_UPDATE(90); if (appInitiatedRestart) { RestoreStateForAppInitiatedRestart(); } else { char* noEMRestart = PR_GetEnv("NO_EM_RESTART"); if (noEMRestart && *noEMRestart) { SaveToEnv("NO_EM_RESTART=1"); } else { SaveToEnv("NO_EM_RESTART=0"); } } // Ensure that these environment variables are set: SaveFileToEnvIfUnset("XRE_PROFILE_PATH", profD); SaveFileToEnvIfUnset("XRE_PROFILE_LOCAL_PATH", profLD); SaveWordToEnvIfUnset("XRE_PROFILE_NAME", profileName); #ifdef XP_MACOSX if (gBinaryPath) { static char kEnvVar[MAXPATHLEN]; sprintf(kEnvVar, "XRE_BINARY_PATH=%s", gBinaryPath); PR_SetEnv(kEnvVar); } #endif #if defined(HAVE_DESKTOP_STARTUP_ID) && defined(MOZ_WIDGET_GTK2) if (!desktopStartupID.IsEmpty()) { nsCAutoString desktopStartupEnv; desktopStartupEnv.AssignLiteral("DESKTOP_STARTUP_ID="); desktopStartupEnv.Append(desktopStartupID); // Leak it with extreme prejudice! PR_SetEnv(ToNewCString(desktopStartupEnv)); } #endif #ifdef MOZ_WIDGET_GTK2 MOZ_gdk_display_close(display); #endif rv = LaunchChild(nativeApp, appInitiatedRestart); #ifdef MOZ_CRASHREPORTER if (appData.flags & NS_XRE_ENABLE_CRASH_REPORTER) CrashReporter::UnsetExceptionHandler(); #endif return rv == NS_ERROR_LAUNCHED_CHILD_PROCESS ? 0 : 1; } #ifdef MOZ_WIDGET_GTK2 // gdk_display_close also calls gdk_display_manager_set_default_display // appropriately when necessary. MOZ_gdk_display_close(display); #endif } #ifdef MOZ_CRASHREPORTER if (appData.flags & NS_XRE_ENABLE_CRASH_REPORTER) CrashReporter::UnsetExceptionHandler(); #endif XRE_DeinitCommandLine(); return NS_FAILED(rv) ? 1 : 0; } nsresult XRE_InitCommandLine(int aArgc, char* aArgv[]) { nsresult rv = NS_OK; #if defined(MOZ_IPC) #if defined(OS_WIN) CommandLine::Init(aArgc, aArgv); #else // these leak on error, but that's OK: we'll just exit() char** canonArgs = new char*[aArgc]; // get the canonical version of the binary's path nsCOMPtr binFile; rv = XRE_GetBinaryPath(aArgv[0], getter_AddRefs(binFile)); if (NS_FAILED(rv)) return NS_ERROR_FAILURE; nsCAutoString canonBinPath; rv = binFile->GetNativePath(canonBinPath); if (NS_FAILED(rv)) return NS_ERROR_FAILURE; canonArgs[0] = strdup(canonBinPath.get()); for (int i = 1; i < aArgc; ++i) { if (aArgv[i]) { canonArgs[i] = strdup(aArgv[i]); } } NS_ASSERTION(!CommandLine::IsInitialized(), "Bad news!"); CommandLine::Init(aArgc, canonArgs); for (int i = 0; i < aArgc; ++i) free(canonArgs[i]); delete[] canonArgs; #endif #endif return rv; } nsresult XRE_DeinitCommandLine() { nsresult rv = NS_OK; #if defined(MOZ_IPC) CommandLine::Terminate(); #endif return rv; } GeckoProcessType XRE_GetProcessType() { #ifdef MOZ_IPC return mozilla::startup::sChildProcessType; #else return GeckoProcessType_Default; #endif } void SetupErrorHandling(const char* progname) { #ifdef XP_WIN /* On Windows XPSP3 and Windows Vista if DEP is configured off-by-default we still want DEP protection: enable it explicitly and programmatically. This function is not available on WinXPSP2 so we dynamically load it. */ HMODULE kernel32 = GetModuleHandleW(L"kernel32.dll"); SetProcessDEPPolicyFunc _SetProcessDEPPolicy = (SetProcessDEPPolicyFunc) GetProcAddress(kernel32, "SetProcessDEPPolicy"); if (_SetProcessDEPPolicy) _SetProcessDEPPolicy(PROCESS_DEP_ENABLE); #endif #if defined (XP_WIN32) && !defined (WINCE) // Suppress the "DLL Foo could not be found" dialog, such that if dependent // libraries (such as GDI+) are not preset, we gracefully fail to load those // XPCOM components, instead of being ungraceful. UINT realMode = SetErrorMode(0); realMode |= SEM_FAILCRITICALERRORS; // If XRE_NO_WINDOWS_CRASH_DIALOG is set, suppress displaying the "This // application has crashed" dialog box. This is mainly useful for // automated testing environments, e.g. tinderbox, where there's no need // for a dozen of the dialog boxes to litter the console if (getenv("XRE_NO_WINDOWS_CRASH_DIALOG")) realMode |= SEM_NOGPFAULTERRORBOX | SEM_NOOPENFILEERRORBOX; SetErrorMode(realMode); #endif #ifndef XP_OS2 InstallSignalHandlers(progname); #endif #ifndef WINCE // Unbuffer stdout, needed for tinderbox tests. setbuf(stdout, 0); #endif #if defined(FREEBSD) // Disable all SIGFPE's on FreeBSD, as it has non-IEEE-conformant fp // trap behavior that trips up on floating-point tests performed by // the JS engine. See bugzilla bug 9967 details. fpsetmask(0); #endif }