зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1675329: Add an API for returning an exit code from the application. r=dthayer,necko-reviewers,dragana,nika
Callers can pass an exit code to nsIAppStartup::Quit and it will be returned from the process when it exits. Note that I have using uint16_t as the exit code because on Windows the exit code can be a uint and elsewhere it is an int. A uint16_t will safely convert to either of those and no-one will ever need more than 64k exit codes! Differential Revision: https://phabricator.services.mozilla.com/D96857
This commit is contained in:
Родитель
ec28416cf3
Коммит
a3cb03324e
|
@ -103,7 +103,7 @@ NS_IMETHODIMP nsReadConfig::Observe(nsISupports* aSubject, const char* aTopic,
|
|||
components::AppStartup::Service();
|
||||
if (appStartup) {
|
||||
bool userAllowedQuit = true;
|
||||
appStartup->Quit(nsIAppStartup::eAttemptQuit, &userAllowedQuit);
|
||||
appStartup->Quit(nsIAppStartup::eAttemptQuit, 0, &userAllowedQuit);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -88,7 +88,7 @@ void SocketProcessParent::ActorDestroy(ActorDestroyReason aWhy) {
|
|||
do_GetService("@mozilla.org/toolkit/app-startup;1");
|
||||
if (appService) {
|
||||
bool userAllowedQuit = true;
|
||||
appService->Quit(nsIAppStartup::eForceQuit, &userAllowedQuit);
|
||||
appService->Quit(nsIAppStartup::eForceQuit, 1, &userAllowedQuit);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -275,7 +275,7 @@ nsAppStartup::Run(void) {
|
|||
// regardless of whether the event loop has spun or not. Note that this call
|
||||
// is a no-op if Quit has already been called previously.
|
||||
bool userAllowedQuit = true;
|
||||
Quit(eForceQuit, &userAllowedQuit);
|
||||
Quit(eForceQuit, 0, &userAllowedQuit);
|
||||
|
||||
nsresult retval = NS_OK;
|
||||
if (mozilla::AppShutdown::IsRestarting()) {
|
||||
|
@ -286,7 +286,7 @@ nsAppStartup::Run(void) {
|
|||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsAppStartup::Quit(uint32_t aMode, bool* aUserAllowedQuit) {
|
||||
nsAppStartup::Quit(uint32_t aMode, int aExitCode, bool* aUserAllowedQuit) {
|
||||
uint32_t ferocity = (aMode & 0xF);
|
||||
|
||||
// If the shutdown was cancelled due to a hidden window or
|
||||
|
@ -371,7 +371,7 @@ nsAppStartup::Quit(uint32_t aMode, bool* aUserAllowedQuit) {
|
|||
auto shutdownMode = ((aMode & eRestart) != 0)
|
||||
? mozilla::AppShutdownMode::Restart
|
||||
: mozilla::AppShutdownMode::Normal;
|
||||
mozilla::AppShutdown::Init(shutdownMode);
|
||||
mozilla::AppShutdown::Init(shutdownMode, aExitCode);
|
||||
|
||||
if (mozilla::AppShutdown::IsRestarting()) {
|
||||
// Mark the next startup as a restart.
|
||||
|
@ -506,7 +506,12 @@ nsAppStartup::ExitLastWindowClosingSurvivalArea(void) {
|
|||
|
||||
if (mRunning) {
|
||||
bool userAllowedQuit = false;
|
||||
Quit(eConsiderQuit, &userAllowedQuit);
|
||||
|
||||
// A previous call to Quit may have told all windows to close and then
|
||||
// bailed out waiting for that to happen. This is how we get back into Quit
|
||||
// after each window closes so the exit process can continue when ready.
|
||||
// Make sure to pass along the exit code that was initially passed to Quit.
|
||||
Quit(eConsiderQuit, mozilla::AppShutdown::GetExitCode(), &userAllowedQuit);
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
|
@ -952,7 +957,7 @@ NS_IMETHODIMP
|
|||
nsAppStartup::RestartInSafeMode(uint32_t aQuitMode) {
|
||||
PR_SetEnv("MOZ_SAFE_MODE_RESTART=1");
|
||||
bool userAllowedQuit = false;
|
||||
this->Quit(aQuitMode | nsIAppStartup::eRestart, &userAllowedQuit);
|
||||
this->Quit(aQuitMode | nsIAppStartup::eRestart, 0, &userAllowedQuit);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
|
|
@ -123,12 +123,17 @@ interface nsIAppStartup : nsISupports
|
|||
* @param aMode
|
||||
* This parameter modifies how the app is shutdown, and it is
|
||||
* constructed from the constants defined above.
|
||||
* @param aExitCode
|
||||
* The exit code to return from the process. The precise code
|
||||
* returned by the process may vary depending on the platform. Only
|
||||
* values 0-255 should generallt be used. If not specified an exit
|
||||
* code of 0 will be used.
|
||||
*
|
||||
* @return false if the shutdown was cancelled due to the presence
|
||||
* of a hidden window or if the user disallowed a window
|
||||
* to be closed.
|
||||
*/
|
||||
bool quit(in uint32_t aMode);
|
||||
bool quit(in uint32_t aMode, [optional] in int32_t aExitCode);
|
||||
|
||||
/**
|
||||
* True if the application is in the process of shutting down.
|
||||
|
|
|
@ -304,7 +304,7 @@ void ProcessPendingGetURLAppleEvents() {
|
|||
nsCOMPtr<nsIAppStartup> appService = do_GetService("@mozilla.org/toolkit/app-startup;1");
|
||||
if (appService) {
|
||||
bool userAllowedQuit = true;
|
||||
appService->Quit(nsIAppStartup::eForceQuit, &userAllowedQuit);
|
||||
appService->Quit(nsIAppStartup::eForceQuit, 0, &userAllowedQuit);
|
||||
if (!userAllowedQuit) {
|
||||
return NSTerminateCancel;
|
||||
}
|
||||
|
|
|
@ -2207,9 +2207,9 @@ nsresult LaunchChild(bool aBlankCommandLine) {
|
|||
PRStatus failed = PR_WaitProcess(process, &exitCode);
|
||||
if (failed || exitCode) return NS_ERROR_FAILURE;
|
||||
# endif // XP_UNIX
|
||||
# endif // WP_WIN
|
||||
# endif // WP_MACOSX
|
||||
#endif // MOZ_WIDGET_ANDROID
|
||||
# endif // WP_WIN
|
||||
# endif // WP_MACOSX
|
||||
#endif // MOZ_WIDGET_ANDROID
|
||||
|
||||
return NS_ERROR_LAUNCHED_CHILD_PROCESS;
|
||||
}
|
||||
|
@ -4154,7 +4154,7 @@ int XREMain::XRE_mainStartup(bool* aExitFlag) {
|
|||
}
|
||||
}
|
||||
# endif /* DEBUG */
|
||||
#endif /* FUZZING */
|
||||
#endif /* FUZZING */
|
||||
|
||||
#if defined(XP_WIN)
|
||||
// Enable the HeapEnableTerminationOnCorruption exploit mitigation. We ignore
|
||||
|
@ -4691,7 +4691,7 @@ nsresult XREMain::XRE_mainRun() {
|
|||
# if defined(MOZ_GECKO_PROFILER)
|
||||
mozilla::mscom::InitProfilerMarkers();
|
||||
# endif // defined(MOZ_GECKO_PROFILER)
|
||||
#endif // defined(XP_WIN)
|
||||
#endif // defined(XP_WIN)
|
||||
|
||||
rv = mScopedXPCOM->SetWindowCreator(mNativeApp);
|
||||
NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
|
||||
|
@ -5342,7 +5342,10 @@ int XREMain::XRE_main(int argc, char* argv[], const BootstrapConfig& aConfig) {
|
|||
|
||||
XRE_DeinitCommandLine();
|
||||
|
||||
return NS_FAILED(rv) ? 1 : 0;
|
||||
if (NS_FAILED(rv)) {
|
||||
return 1;
|
||||
}
|
||||
return mozilla::AppShutdown::GetExitCode();
|
||||
}
|
||||
|
||||
void XRE_StopLateWriteChecks(void) { mozilla::StopLateWriteChecks(); }
|
||||
|
|
|
@ -1 +1,2 @@
|
|||
[test_fission_autostart.py]
|
||||
[test_exitcode.py]
|
||||
|
|
|
@ -0,0 +1,33 @@
|
|||
from marionette_harness import MarionetteTestCase
|
||||
|
||||
|
||||
class TestFissionAutostart(MarionetteTestCase):
|
||||
def test_normal_exit(self):
|
||||
self.marionette.set_context(self.marionette.CONTEXT_CHROME)
|
||||
|
||||
def call_quit():
|
||||
self.marionette.execute_script(
|
||||
"""
|
||||
const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
|
||||
Services.startup.quit(Ci.nsIAppStartup.eAttemptQuit);
|
||||
""",
|
||||
sandbox="system",
|
||||
)
|
||||
|
||||
self.marionette.quit(in_app=True, callback=call_quit)
|
||||
self.assertEqual(self.marionette.instance.runner.returncode, 0)
|
||||
|
||||
def test_exit_code(self):
|
||||
self.marionette.set_context(self.marionette.CONTEXT_CHROME)
|
||||
|
||||
def call_quit():
|
||||
self.marionette.execute_script(
|
||||
"""
|
||||
const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
|
||||
Services.startup.quit(Ci.nsIAppStartup.eAttemptQuit, 5);
|
||||
""",
|
||||
sandbox="system",
|
||||
)
|
||||
|
||||
self.marionette.quit(in_app=True, callback=call_quit)
|
||||
self.assertEqual(self.marionette.instance.runner.returncode, 5)
|
|
@ -917,7 +917,7 @@ static BOOL gMenuItemsExecuteCommands = YES;
|
|||
nsCOMPtr<nsIAppStartup> appStartup = mozilla::components::AppStartup::Service();
|
||||
if (appStartup) {
|
||||
bool userAllowedQuit = true;
|
||||
appStartup->Quit(nsIAppStartup::eAttemptQuit, &userAllowedQuit);
|
||||
appStartup->Quit(nsIAppStartup::eAttemptQuit, 0, &userAllowedQuit);
|
||||
}
|
||||
}
|
||||
return;
|
||||
|
|
|
@ -37,6 +37,7 @@ static ShutdownPhase sFastShutdownPhase = ShutdownPhase::NotInShutdown;
|
|||
static ShutdownPhase sLateWriteChecksPhase = ShutdownPhase::NotInShutdown;
|
||||
static AppShutdownMode sShutdownMode = AppShutdownMode::Normal;
|
||||
static Atomic<bool, MemoryOrdering::Relaxed> sIsShuttingDown;
|
||||
static int sExitCode = 0;
|
||||
|
||||
// These environment variable strings are all deliberately copied and leaked
|
||||
// due to requirements of PR_SetEnv and similar.
|
||||
|
@ -66,6 +67,8 @@ ShutdownPhase GetShutdownPhaseFromPrefValue(int32_t aPrefValue) {
|
|||
|
||||
bool AppShutdown::IsShuttingDown() { return sIsShuttingDown; }
|
||||
|
||||
int AppShutdown::GetExitCode() { return sExitCode; }
|
||||
|
||||
void AppShutdown::SaveEnvVarsForPotentialRestart() {
|
||||
const char* s = PR_GetEnv("XUL_APP_FILE");
|
||||
if (s) {
|
||||
|
@ -124,11 +127,13 @@ wchar_t* CopyPathIntoNewWCString(nsIFile* aFile) {
|
|||
}
|
||||
#endif
|
||||
|
||||
void AppShutdown::Init(AppShutdownMode aMode) {
|
||||
void AppShutdown::Init(AppShutdownMode aMode, int aExitCode) {
|
||||
if (sShutdownMode == AppShutdownMode::Normal) {
|
||||
sShutdownMode = aMode;
|
||||
}
|
||||
|
||||
sExitCode = aExitCode;
|
||||
|
||||
// Late-write checks needs to find the profile directory, so it has to
|
||||
// be initialized before services::Shutdown or (because of
|
||||
// xpcshell tests replacing the service) modules being unloaded.
|
||||
|
@ -184,7 +189,7 @@ void AppShutdown::MaybeFastShutdown(ShutdownPhase aPhase) {
|
|||
profiler_shutdown(IsFastShutdown::Yes);
|
||||
#endif
|
||||
|
||||
DoImmediateExit();
|
||||
DoImmediateExit(sExitCode);
|
||||
} else if (aPhase == sLateWriteChecksPhase) {
|
||||
#ifdef XP_MACOSX
|
||||
OnlyReportDirtyWrites();
|
||||
|
@ -221,15 +226,15 @@ void AppShutdown::OnShutdownConfirmed() {
|
|||
}
|
||||
}
|
||||
|
||||
void AppShutdown::DoImmediateExit() {
|
||||
void AppShutdown::DoImmediateExit(int aExitCode) {
|
||||
#ifdef XP_WIN
|
||||
HANDLE process = ::GetCurrentProcess();
|
||||
if (::TerminateProcess(process, 0)) {
|
||||
if (::TerminateProcess(process, aExitCode)) {
|
||||
::WaitForSingleObject(process, INFINITE);
|
||||
}
|
||||
MOZ_CRASH("TerminateProcess failed.");
|
||||
#else
|
||||
_exit(0);
|
||||
_exit(aExitCode);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
|
|
@ -20,6 +20,11 @@ class AppShutdown {
|
|||
public:
|
||||
static bool IsShuttingDown();
|
||||
|
||||
/**
|
||||
* Returns the current exit code that the process will be terminated with.
|
||||
*/
|
||||
static int GetExitCode();
|
||||
|
||||
/**
|
||||
* Save environment variables that we might need if the app initiates a
|
||||
* restart later in its lifecycle.
|
||||
|
@ -27,9 +32,9 @@ class AppShutdown {
|
|||
static void SaveEnvVarsForPotentialRestart();
|
||||
|
||||
/**
|
||||
* Init the shutdown with the requested shutdown mode.
|
||||
* Init the shutdown with the requested shutdown mode and exit code.
|
||||
*/
|
||||
static void Init(AppShutdownMode aMode);
|
||||
static void Init(AppShutdownMode aMode, int aExitCode);
|
||||
|
||||
/**
|
||||
* Confirm that we are in fact going to be shutting down.
|
||||
|
@ -54,8 +59,10 @@ class AppShutdown {
|
|||
* destructors for xul.dll.
|
||||
*
|
||||
* This method terminates the current process without those issues.
|
||||
*
|
||||
* Optionally a custom exit code can be supplied.
|
||||
*/
|
||||
static void DoImmediateExit();
|
||||
static void DoImmediateExit(int aExitCode = 0);
|
||||
|
||||
/**
|
||||
* True if the application is currently attempting to shut down in order to
|
||||
|
|
Загрузка…
Ссылка в новой задаче