mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-10 11:55:49 +00:00
fixes bug 297862 "Provide an API to restart the application" r=bsmedberg a=asa
This commit is contained in:
parent
f0a85660c4
commit
c043681487
@ -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)
|
||||
%}
|
||||
|
@ -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
|
||||
|
@ -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__
|
||||
|
@ -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;
|
||||
|
Loading…
Reference in New Issue
Block a user