fixes bug 297862 "Provide an API to restart the application" r=bsmedberg a=asa

This commit is contained in:
darin%meer.net 2005-06-20 23:18:00 +00:00
parent f0a85660c4
commit c043681487
4 changed files with 127 additions and 24 deletions

View File

@ -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)
%}

View File

@ -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<nsIWindowMediator> 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<nsISupports> window;
windowEnumerator->GetNext(getter_AddRefs(window));
nsCOMPtr<nsIDOMWindowInternal> 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

View File

@ -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__

View File

@ -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;