diff --git a/toolkit/components/startup/public/nsIAppStartup.idl b/toolkit/components/startup/public/nsIAppStartup.idl index 16024ff78e26..a7b3bcaa1c21 100644 --- a/toolkit/components/startup/public/nsIAppStartup.idl +++ b/toolkit/components/startup/public/nsIAppStartup.idl @@ -52,6 +52,10 @@ interface nsIAppStartup : nsISupports /** * Runs an application event loop: normally the main event pump which * defines the lifetime of the application. + * + * @returnCode NS_SUCCESS_RESTART_APP + * This return code indicates that the application should be + * restarted because quit was called with the eRestart flag. */ void run(); @@ -66,12 +70,50 @@ interface nsIAppStartup : nsISupports void enterLastWindowClosingSurvivalArea(); void exitLastWindowClosingSurvivalArea(); - const PRUint32 eConsiderQuit = 1; // attempt to quit if all windows are closed - const PRUint32 eAttemptQuit = 2; // try to close all windows, then quit if successful - const PRUint32 eForceQuit = 3; // quit, damnit + /** + * The following flags may be passed as the aMode parameter to the quit + * method. One and only one of the "Quit" flags must be specified. The + * eRestart flag may be bit-wise combined with one of the "Quit" flags to + * cause the application to restart after it quits. + */ /** - * Exit the event loop, shut down the app + * Attempt to quit if all windows are closed. */ - void quit(in PRUint32 aFerocity); + const PRUint32 eConsiderQuit = 0x01; + + /** + * Try to close all windows, then quit if successful. + */ + const PRUint32 eAttemptQuit = 0x02; + + /** + * Quit, damnit! + */ + const PRUint32 eForceQuit = 0x03; + + /** + * Restart the application after quitting. The application will be + * restarted with the same profile and an empty command line. + */ + const PRUint32 eRestart = 0x10; + + /** + * Exit the event loop, and shut down the app. + * + * @param aMode + * This parameter modifies how the app is shutdown, and it is + * constructed from the constants defined above. + */ + void quit(in PRUint32 aMode); }; + +%{C++ +/** + * This success code may be returned by nsIAppStartup::Run to indicate that the + * application should be restarted. This condition corresponds to the case in + * which nsIAppStartup::Quit was called with the eRestart flag. + */ +#define NS_SUCCESS_RESTART_APP \ + NS_ERROR_GENERATE_SUCCESS(NS_ERROR_MODULE_GENERAL, 1) +%} diff --git a/toolkit/components/startup/src/nsAppStartup.cpp b/toolkit/components/startup/src/nsAppStartup.cpp index ac2a0a008b4c..c1c4b7177438 100644 --- a/toolkit/components/startup/src/nsAppStartup.cpp +++ b/toolkit/components/startup/src/nsAppStartup.cpp @@ -78,7 +78,8 @@ NS_DEFINE_CID(kAppShellCID, NS_APPSHELL_CID); nsAppStartup::nsAppStartup() : mConsiderQuitStopper(0), mShuttingDown(PR_FALSE), - mAttemptingQuit(PR_FALSE) + mAttemptingQuit(PR_FALSE), + mRestart(PR_FALSE) { } @@ -141,15 +142,21 @@ nsAppStartup::CreateHiddenWindow() NS_IMETHODIMP nsAppStartup::Run(void) { - return mAppShell->Run(); + nsresult rv = mAppShell->Run(); + if (NS_FAILED(rv)) + return rv; + + return mRestart ? NS_SUCCESS_RESTART_APP : NS_OK; } NS_IMETHODIMP -nsAppStartup::Quit(PRUint32 aFerocity) +nsAppStartup::Quit(PRUint32 aMode) { + PRUint32 ferocity = (aMode & 0xF); + // Quit the application. We will asynchronously call the appshell's - // Exit() method via the ExitCallback() to allow one last pass + // Exit() method via HandleExitEvent() to allow one last pass // through any events in the queue. This guarantees a tidy cleanup. nsresult rv = NS_OK; PRBool postedExitEvent = PR_FALSE; @@ -160,17 +167,18 @@ nsAppStartup::Quit(PRUint32 aFerocity) /* eForceQuit doesn't actually work; it can cause a subtle crash if there are windows open which have unload handlers which open new windows. Use eAttemptQuit for now. */ - if (aFerocity == eForceQuit) { + if (ferocity == eForceQuit) { NS_WARNING("attempted to force quit"); // it will be treated the same as eAttemptQuit, below } mShuttingDown = PR_TRUE; + mRestart = aMode & eRestart; nsCOMPtr mediator (do_GetService(NS_WINDOWMEDIATOR_CONTRACTID)); - if (aFerocity == eConsiderQuit && mConsiderQuitStopper == 0) { + if (ferocity == eConsiderQuit && mConsiderQuitStopper == 0) { // attempt quit if the last window has been unregistered/closed PRBool windowsRemain = PR_TRUE; @@ -182,15 +190,15 @@ nsAppStartup::Quit(PRUint32 aFerocity) windowEnumerator->HasMoreElements(&windowsRemain); } if (!windowsRemain) { - aFerocity = eAttemptQuit; + ferocity = eAttemptQuit; } } - /* Currently aFerocity can never have the value of eForceQuit here. + /* Currently ferocity can never have the value of eForceQuit here. That's temporary (in an unscheduled kind of way) and logically this code is part of the eForceQuit case, so I'm checking against that value anyway. Reviewers made me add this comment. */ - if (aFerocity == eAttemptQuit || aFerocity == eForceQuit) { + if (ferocity == eAttemptQuit || ferocity == eForceQuit) { AttemptingQuit(PR_TRUE); @@ -224,9 +232,9 @@ nsAppStartup::Quit(PRUint32 aFerocity) } } - if (aFerocity == eAttemptQuit) { + if (ferocity == eAttemptQuit) { - aFerocity = eForceQuit; // assume success + ferocity = eForceQuit; // assume success /* Were we able to immediately close all windows? if not, eAttemptQuit failed. This could happen for a variety of reasons; in fact it's @@ -240,7 +248,7 @@ nsAppStartup::Quit(PRUint32 aFerocity) while (windowEnumerator->HasMoreElements(&more), more) { /* we can't quit immediately. we'll try again as the last window finally closes. */ - aFerocity = eAttemptQuit; + ferocity = eAttemptQuit; nsCOMPtr window; windowEnumerator->GetNext(getter_AddRefs(window)); nsCOMPtr domWindow(do_QueryInterface(window)); @@ -258,7 +266,7 @@ nsAppStartup::Quit(PRUint32 aFerocity) } } - if (aFerocity == eForceQuit) { + if (ferocity == eForceQuit) { // do it! // No chance of the shutdown being cancelled from here on; tell people diff --git a/toolkit/components/startup/src/nsAppStartup.h b/toolkit/components/startup/src/nsAppStartup.h index b6f62739a51a..06634cf6a342 100644 --- a/toolkit/components/startup/src/nsAppStartup.h +++ b/toolkit/components/startup/src/nsAppStartup.h @@ -84,6 +84,7 @@ private: PRInt32 mConsiderQuitStopper; // if > 0, Quit(eConsiderQuit) fails PRPackedBool mShuttingDown; // Quit method reentrancy check PRPackedBool mAttemptingQuit; // Quit(eAttemptQuit) still trying + PRPackedBool mRestart; // Quit(eRestart) }; #endif // nsAppStartup_h__ diff --git a/toolkit/xre/nsAppRunner.cpp b/toolkit/xre/nsAppRunner.cpp index f73fb4dccc6d..6ab955468089 100644 --- a/toolkit/xre/nsAppRunner.cpp +++ b/toolkit/xre/nsAppRunner.cpp @@ -1133,12 +1133,21 @@ XRE_GetBinaryPath(const char* argv0, nsILocalFile* *aResult) #define NS_ERROR_LAUNCHED_CHILD_PROCESS NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_PROFILE, 200) -static nsresult LaunchChild(nsINativeAppSupport* aNative) +// 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; + } #if defined(XP_MACOSX) LaunchChildMac(gRestartArgc, gRestartArgv); @@ -1678,6 +1687,34 @@ static void RemoveComponentRegistries(nsIFile* aProfileDir, nsIFile* aLocalProfi 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); + } +} + const nsXREAppData* gAppData = nsnull; #if defined(XP_OS2) @@ -1996,6 +2033,7 @@ XRE_main(int argc, char* argv[], const nsXREAppData* aAppData) } PRBool needsRestart = PR_FALSE; + PRBool appInitiatedRestart = PR_FALSE; // Allows the user to forcefully bypass the restart process at their // own risk. Useful for debugging or for tinderboxes where child @@ -2103,6 +2141,7 @@ XRE_main(int argc, char* argv[], const nsXREAppData* aAppData) } 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. @@ -2177,6 +2216,13 @@ XRE_main(int argc, char* argv[], const nsXREAppData* aAppData) gLogConsoleErrors = PR_TRUE; } + // 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; + } + #ifdef MOZ_ENABLE_XREMOTE // shut down the x remote proxy window if (remoteService) @@ -2204,12 +2250,17 @@ XRE_main(int argc, char* argv[], const nsXREAppData* aAppData) // Restart the app after XPCOM has been shut down cleanly. if (needsRestart) { - char* noEMRestart = PR_GetEnv("NO_EM_RESTART"); - if (noEMRestart && *noEMRestart) { - PR_SetEnv("NO_EM_RESTART=1"); + if (appInitiatedRestart) { + RestoreStateForAppInitiatedRestart(); } else { - PR_SetEnv("NO_EM_RESTART=0"); + char* noEMRestart = PR_GetEnv("NO_EM_RESTART"); + if (noEMRestart && *noEMRestart) { + PR_SetEnv("NO_EM_RESTART=1"); + } + else { + PR_SetEnv("NO_EM_RESTART=0"); + } } nsCAutoString path1, path2; @@ -2222,7 +2273,8 @@ XRE_main(int argc, char* argv[], const nsXREAppData* aAppData) PR_SetEnv(kEnvVar1); PR_SetEnv(kEnvVar2); - return LaunchChild(nativeApp) == NS_ERROR_LAUNCHED_CHILD_PROCESS ? 0 : 1; + rv = LaunchChild(nativeApp, appInitiatedRestart); + return rv == NS_ERROR_LAUNCHED_CHILD_PROCESS ? 0 : 1; } return NS_FAILED(rv) ? 1 : 0;