diff options
Diffstat (limited to 'toolkit/xre/nsNativeAppSupportUnix.cpp')
-rw-r--r-- | toolkit/xre/nsNativeAppSupportUnix.cpp | 708 |
1 files changed, 708 insertions, 0 deletions
diff --git a/toolkit/xre/nsNativeAppSupportUnix.cpp b/toolkit/xre/nsNativeAppSupportUnix.cpp new file mode 100644 index 000000000..a04a79953 --- /dev/null +++ b/toolkit/xre/nsNativeAppSupportUnix.cpp @@ -0,0 +1,708 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "nsNativeAppSupportBase.h" +#include "nsCOMPtr.h" +#include "nsXPCOM.h" +#include "nsISupportsPrimitives.h" +#include "nsIObserverService.h" +#include "nsIAppStartup.h" +#include "nsServiceManagerUtils.h" +#include "prlink.h" +#include "nsXREDirProvider.h" +#include "nsReadableUtils.h" + +#include "nsIFile.h" +#include "nsDirectoryServiceDefs.h" +#include "nsICommandLineRunner.h" +#include "nsIWindowMediator.h" +#include "nsPIDOMWindow.h" +#include "nsIDocShell.h" +#include "nsIBaseWindow.h" +#include "nsIWidget.h" +#include "nsIWritablePropertyBag2.h" +#include "nsIPrefService.h" +#include "mozilla/Services.h" + +#include <stdlib.h> +#include <glib.h> +#include <glib-object.h> +#include <gtk/gtk.h> + +#ifdef MOZ_X11 +#include <gdk/gdkx.h> +#include <X11/ICE/ICElib.h> +#include <X11/SM/SMlib.h> +#include <fcntl.h> +#include "nsThreadUtils.h" + +#include <pwd.h> +#endif + +#ifdef MOZ_ENABLE_DBUS +#include <dbus/dbus.h> +#endif + +#define MIN_GTK_MAJOR_VERSION 2 +#define MIN_GTK_MINOR_VERSION 10 +#define UNSUPPORTED_GTK_MSG "We're sorry, this application requires a version of the GTK+ library that is not installed on your computer.\n\n\ +You have GTK+ %d.%d.\nThis application requires GTK+ %d.%d or newer.\n\n\ +Please upgrade your GTK+ library if you wish to use this application." + +#if MOZ_X11 +#undef IceSetIOErrorHandler +#undef IceAddConnectionWatch +#undef IceConnectionNumber +#undef IceProcessMessages +#undef IceGetConnectionContext +#undef SmcInteractDone +#undef SmcSaveYourselfDone +#undef SmcInteractRequest +#undef SmcCloseConnection +#undef SmcOpenConnection +#undef SmcSetProperties + +typedef IceIOErrorHandler (*IceSetIOErrorHandlerFn) (IceIOErrorHandler); +typedef int (*IceAddConnectionWatchFn) (IceWatchProc, IcePointer); +typedef int (*IceConnectionNumberFn) (IceConn); +typedef IceProcessMessagesStatus (*IceProcessMessagesFn) (IceConn, IceReplyWaitInfo*, Bool*); +typedef IcePointer (*IceGetConnectionContextFn) (IceConn); + +typedef void (*SmcInteractDoneFn) (SmcConn, Bool); +typedef void (*SmcSaveYourselfDoneFn) (SmcConn, Bool); +typedef int (*SmcInteractRequestFn) (SmcConn, int, SmcInteractProc, SmPointer); +typedef SmcCloseStatus (*SmcCloseConnectionFn) (SmcConn, int, char**); +typedef SmcConn (*SmcOpenConnectionFn) (char*, SmPointer, int, int, + unsigned long, SmcCallbacks*, + const char*, char**, int, char*); +typedef void (*SmcSetPropertiesFn) (SmcConn, int, SmProp**); + +static IceSetIOErrorHandlerFn IceSetIOErrorHandlerPtr; +static IceAddConnectionWatchFn IceAddConnectionWatchPtr; +static IceConnectionNumberFn IceConnectionNumberPtr; +static IceProcessMessagesFn IceProcessMessagesPtr; +static IceGetConnectionContextFn IceGetConnectionContextPtr; +static SmcInteractDoneFn SmcInteractDonePtr; +static SmcSaveYourselfDoneFn SmcSaveYourselfDonePtr; +static SmcInteractRequestFn SmcInteractRequestPtr; +static SmcCloseConnectionFn SmcCloseConnectionPtr; +static SmcOpenConnectionFn SmcOpenConnectionPtr; +static SmcSetPropertiesFn SmcSetPropertiesPtr; + +#define IceSetIOErrorHandler IceSetIOErrorHandlerPtr +#define IceAddConnectionWatch IceAddConnectionWatchPtr +#define IceConnectionNumber IceConnectionNumberPtr +#define IceProcessMessages IceProcessMessagesPtr +#define IceGetConnectionContext IceGetConnectionContextPtr +#define SmcInteractDone SmcInteractDonePtr +#define SmcSaveYourselfDone SmcSaveYourselfDonePtr +#define SmcInteractRequest SmcInteractRequestPtr +#define SmcCloseConnection SmcCloseConnectionPtr +#define SmcOpenConnection SmcOpenConnectionPtr +#define SmcSetProperties SmcSetPropertiesPtr + +enum ClientState { + STATE_DISCONNECTED, + STATE_REGISTERING, + STATE_IDLE, + STATE_INTERACTING, + STATE_SHUTDOWN_CANCELLED +}; + +static const char *gClientStateTable[] = { + "DISCONNECTED", + "REGISTERING", + "IDLE", + "INTERACTING", + "SHUTDOWN_CANCELLED" +}; + +static LazyLogModule sMozSMLog("MozSM"); +#endif /* MOZ_X11 */ + +class nsNativeAppSupportUnix : public nsNativeAppSupportBase +{ +public: +#if MOZ_X11 + nsNativeAppSupportUnix(): mSessionConnection(nullptr), + mClientState(STATE_DISCONNECTED) {}; + ~nsNativeAppSupportUnix() + { + // this goes out of scope after "web-workers-shutdown" async shutdown phase + // so it's safe to disconnect here (i.e. the application won't lose data) + DisconnectFromSM(); + }; + + void DisconnectFromSM(); +#endif + NS_IMETHOD Start(bool* aRetVal); + NS_IMETHOD Stop(bool *aResult); + NS_IMETHOD Enable(); + +private: +#if MOZ_X11 + static void SaveYourselfCB(SmcConn smc_conn, SmPointer client_data, + int save_style, Bool shutdown, int interact_style, + Bool fast); + static void DieCB(SmcConn smc_conn, SmPointer client_data); + static void InteractCB(SmcConn smc_conn, SmPointer client_data); + static void SaveCompleteCB(SmcConn smc_conn, SmPointer client_data) {}; + static void ShutdownCancelledCB(SmcConn smc_conn, SmPointer client_data); + void DoInteract(); + void SetClientState(ClientState aState) + { + mClientState = aState; + MOZ_LOG(sMozSMLog, LogLevel::Debug, ("New state = %s\n", gClientStateTable[aState])); + } + + SmcConn mSessionConnection; + ClientState mClientState; +#endif +}; + +#if MOZ_X11 +static gboolean +process_ice_messages(IceConn connection) +{ + IceProcessMessagesStatus status; + + status = IceProcessMessages(connection, nullptr, nullptr); + + switch (status) { + case IceProcessMessagesSuccess: + return TRUE; + + case IceProcessMessagesIOError: { + nsNativeAppSupportUnix *native = + static_cast<nsNativeAppSupportUnix *>(IceGetConnectionContext(connection)); + native->DisconnectFromSM(); + } + return FALSE; + + case IceProcessMessagesConnectionClosed: + return FALSE; + + default: + g_assert_not_reached (); + } +} + +static gboolean +ice_iochannel_watch(GIOChannel *channel, GIOCondition condition, + gpointer client_data) +{ + return process_ice_messages(static_cast<IceConn>(client_data)); +} + +static void +ice_connection_watch(IceConn connection, IcePointer client_data, + Bool opening, IcePointer *watch_data) +{ + guint watch_id; + + if (opening) { + GIOChannel *channel; + int fd = IceConnectionNumber(connection); + + fcntl(fd, F_SETFD, fcntl(fd, F_GETFD, 0) | FD_CLOEXEC); + channel = g_io_channel_unix_new(fd); + watch_id = g_io_add_watch(channel, + static_cast<GIOCondition>(G_IO_IN | G_IO_ERR), + ice_iochannel_watch, connection); + g_io_channel_unref(channel); + + *watch_data = GUINT_TO_POINTER(watch_id); + } else { + watch_id = GPOINTER_TO_UINT(*watch_data); + g_source_remove(watch_id); + } +} + +static void +ice_io_error_handler(IceConn connection) +{ + // override the default handler which would exit the application; + // do nothing and let ICELib handle the failure of the connection gracefully. +} + +static void +ice_init(void) +{ + static bool initted = false; + + if (!initted) { + IceSetIOErrorHandler(ice_io_error_handler); + IceAddConnectionWatch(ice_connection_watch, nullptr); + initted = true; + } +} + +void +nsNativeAppSupportUnix::InteractCB(SmcConn smc_conn, SmPointer client_data) +{ + nsNativeAppSupportUnix *self = + static_cast<nsNativeAppSupportUnix *>(client_data); + + self->SetClientState(STATE_INTERACTING); + + // We do this asynchronously, as we spin the event loop recursively if + // a dialog is displayed. If we do this synchronously, we don't finish + // processing the current ICE event whilst the dialog is displayed, which + // means we won't process any more. libsm hates us if we do the InteractDone + // with a pending ShutdownCancelled, and we would certainly like to handle Die + // whilst a dialog is displayed + NS_DispatchToCurrentThread(NewRunnableMethod(self, &nsNativeAppSupportUnix::DoInteract)); +} + +void +nsNativeAppSupportUnix::DoInteract() +{ + nsCOMPtr<nsIObserverService> obsServ = + mozilla::services::GetObserverService(); + if (!obsServ) { + SmcInteractDone(mSessionConnection, False); + SmcSaveYourselfDone(mSessionConnection, True); + SetClientState(STATE_IDLE); + return; + } + + nsCOMPtr<nsISupportsPRBool> cancelQuit = + do_CreateInstance(NS_SUPPORTS_PRBOOL_CONTRACTID); + + bool abortQuit = false; + if (cancelQuit) { + cancelQuit->SetData(false); + obsServ->NotifyObservers(cancelQuit, "quit-application-requested", nullptr); + + cancelQuit->GetData(&abortQuit); + } + + if (!abortQuit && mClientState == STATE_DISCONNECTED) { + // The session manager disappeared, whilst we were interacting, so + // quit now + nsCOMPtr<nsIAppStartup> appService = + do_GetService("@mozilla.org/toolkit/app-startup;1"); + + if (appService) { + appService->Quit(nsIAppStartup::eForceQuit); + } + } else { + if (mClientState != STATE_SHUTDOWN_CANCELLED) { + // Only do this if the shutdown wasn't cancelled + SmcInteractDone(mSessionConnection, !!abortQuit); + SmcSaveYourselfDone(mSessionConnection, !abortQuit); + } + + SetClientState(STATE_IDLE); + } +} + +void +nsNativeAppSupportUnix::SaveYourselfCB(SmcConn smc_conn, SmPointer client_data, + int save_style, Bool shutdown, + int interact_style, Bool fast) +{ + nsNativeAppSupportUnix *self = + static_cast<nsNativeAppSupportUnix *>(client_data); + + // Expect a SaveYourselfCB if we're registering a new client. + // All properties are already set in Start() so just reply with + // SmcSaveYourselfDone if the callback matches the expected signature. + // + // Ancient versions (?) of xsm do not follow such an early SaveYourself with + // SaveComplete. This is a problem if the application freezes interaction + // while waiting for a response to SmcSaveYourselfDone. So never freeze + // interaction when in STATE_REGISTERING. + // + // That aside, we could treat each combination of flags appropriately and not + // special-case this. + if (self->mClientState == STATE_REGISTERING) { + self->SetClientState(STATE_IDLE); + + if (save_style == SmSaveLocal && interact_style == SmInteractStyleNone && + !shutdown && !fast) { + SmcSaveYourselfDone(self->mSessionConnection, True); + return; + } + } + + if (self->mClientState == STATE_SHUTDOWN_CANCELLED) { + // The last shutdown request was cancelled whilst we were interacting, + // and we haven't finished interacting yet. Switch the state back again + self->SetClientState(STATE_INTERACTING); + } + + nsCOMPtr<nsIObserverService> obsServ = + mozilla::services::GetObserverService(); + if (!obsServ) { + SmcSaveYourselfDone(smc_conn, True); + return; + } + + bool status = false; + if (save_style != SmSaveGlobal) { + nsCOMPtr<nsISupportsPRBool> didSaveSession = + do_CreateInstance(NS_SUPPORTS_PRBOOL_CONTRACTID); + + if (!didSaveSession) { + SmcSaveYourselfDone(smc_conn, True); + return; + } + + // Notify observers to save the session state + didSaveSession->SetData(false); + obsServ->NotifyObservers(didSaveSession, "session-save", nullptr); + + didSaveSession->GetData(&status); + } + + // If the interact style permits us to, we are shutting down and we didn't + // manage to (or weren't asked to) save the local state, then notify the user + // in advance that we are doing to quit (assuming that we aren't already + // doing so) + if (!status && shutdown && interact_style != SmInteractStyleNone) { + if (self->mClientState != STATE_INTERACTING) { + SmcInteractRequest(smc_conn, SmDialogNormal, + nsNativeAppSupportUnix::InteractCB, client_data); + } + } else { + SmcSaveYourselfDone(smc_conn, True); + } +} + +void +nsNativeAppSupportUnix::DieCB(SmcConn smc_conn, SmPointer client_data) +{ + nsCOMPtr<nsIAppStartup> appService = + do_GetService("@mozilla.org/toolkit/app-startup;1"); + + if (appService) { + appService->Quit(nsIAppStartup::eForceQuit); + } + // Quit causes the shutdown to begin but the shutdown process is asynchronous + // so we can't DisconnectFromSM() yet +} + +void +nsNativeAppSupportUnix::ShutdownCancelledCB(SmcConn smc_conn, + SmPointer client_data) +{ + nsNativeAppSupportUnix *self = + static_cast<nsNativeAppSupportUnix *>(client_data); + + // Interacting is the only time when we wouldn't already have called + // SmcSaveYourselfDone. Do that now, then set the state to make sure we + // don't send it again after finishing interacting + if (self->mClientState == STATE_INTERACTING) { + SmcSaveYourselfDone(smc_conn, False); + self->SetClientState(STATE_SHUTDOWN_CANCELLED); + } +} + +void +nsNativeAppSupportUnix::DisconnectFromSM() +{ + // the SM is free to exit any time after we disconnect, so callers must be + // sure to have reached a sufficiently advanced phase of shutdown that there + // is no risk of data loss: + // e.g. all async writes are complete by the end of "profile-before-change" + if (mSessionConnection) { + SetClientState(STATE_DISCONNECTED); + SmcCloseConnection(mSessionConnection, 0, nullptr); + mSessionConnection = nullptr; + gdk_x11_set_sm_client_id(nullptr); // follow gnome-client behaviour + } +} + +static void +SetSMValue(SmPropValue& val, const nsCString& data) +{ + val.value = static_cast<SmPointer>(const_cast<char*>(data.get())); + val.length = data.Length(); +} + +static void +SetSMProperty(SmProp& prop, const char* name, const char* type, int numVals, + SmPropValue vals[]) +{ + prop.name = const_cast<char*>(name); + prop.type = const_cast<char*>(type); + prop.num_vals = numVals; + prop.vals = vals; +} +#endif /* MOZ_X11 */ + +static void RemoveArg(char **argv) +{ + do { + *argv = *(argv + 1); + ++argv; + } while (*argv); + + --gArgc; +} + +NS_IMETHODIMP +nsNativeAppSupportUnix::Start(bool *aRetVal) +{ + NS_ASSERTION(gAppData, "gAppData must not be null."); + +// The dbus library is used by both nsWifiScannerDBus and BluetoothDBusService, +// from diffrent threads. This could lead to race conditions if the dbus is not +// initialized before making any other library calls. +#ifdef MOZ_ENABLE_DBUS + dbus_threads_init_default(); +#endif + +#if (MOZ_WIDGET_GTK == 2) + if (gtk_major_version < MIN_GTK_MAJOR_VERSION || + (gtk_major_version == MIN_GTK_MAJOR_VERSION && gtk_minor_version < MIN_GTK_MINOR_VERSION)) { + GtkWidget* versionErrDialog = gtk_message_dialog_new(nullptr, + GtkDialogFlags(GTK_DIALOG_MODAL | + GTK_DIALOG_DESTROY_WITH_PARENT), + GTK_MESSAGE_ERROR, + GTK_BUTTONS_OK, + UNSUPPORTED_GTK_MSG, + gtk_major_version, + gtk_minor_version, + MIN_GTK_MAJOR_VERSION, + MIN_GTK_MINOR_VERSION); + gtk_dialog_run(GTK_DIALOG(versionErrDialog)); + gtk_widget_destroy(versionErrDialog); + MozExpectedExit(); + exit(0); + } +#endif + + *aRetVal = true; + +#ifdef MOZ_X11 + gboolean sm_disable = FALSE; + if (!getenv("SESSION_MANAGER")) { + sm_disable = TRUE; + } + + nsAutoCString prev_client_id; + + char **curarg = gArgv + 1; + while (*curarg) { + char *arg = *curarg; + if (arg[0] == '-' && arg[1] == '-') { + arg += 2; + if (!strcmp(arg, "sm-disable")) { + RemoveArg(curarg); + sm_disable = TRUE; + continue; + } else if (!strcmp(arg, "sm-client-id")) { + RemoveArg(curarg); + if (*curarg[0] != '-') { + prev_client_id = *curarg; + RemoveArg(curarg); + } + continue; + } + } + + ++curarg; + } + + if (prev_client_id.IsEmpty()) { + prev_client_id = getenv("DESKTOP_AUTOSTART_ID"); + } + + // We don't want child processes to use the same ID + unsetenv("DESKTOP_AUTOSTART_ID"); + + char *client_id = nullptr; + if (!sm_disable) { + PRLibrary *iceLib = PR_LoadLibrary("libICE.so.6"); + if (!iceLib) { + return NS_OK; + } + + PRLibrary *smLib = PR_LoadLibrary("libSM.so.6"); + if (!smLib) { + PR_UnloadLibrary(iceLib); + return NS_OK; + } + + IceSetIOErrorHandler = (IceSetIOErrorHandlerFn)PR_FindFunctionSymbol(iceLib, "IceSetIOErrorHandler"); + IceAddConnectionWatch = (IceAddConnectionWatchFn)PR_FindFunctionSymbol(iceLib, "IceAddConnectionWatch"); + IceConnectionNumber = (IceConnectionNumberFn)PR_FindFunctionSymbol(iceLib, "IceConnectionNumber"); + IceProcessMessages = (IceProcessMessagesFn)PR_FindFunctionSymbol(iceLib, "IceProcessMessages"); + IceGetConnectionContext = (IceGetConnectionContextFn)PR_FindFunctionSymbol(iceLib, "IceGetConnectionContext"); + if (!IceSetIOErrorHandler || !IceAddConnectionWatch || + !IceConnectionNumber || !IceProcessMessages || !IceGetConnectionContext) { + PR_UnloadLibrary(iceLib); + PR_UnloadLibrary(smLib); + return NS_OK; + } + + SmcInteractDone = (SmcInteractDoneFn)PR_FindFunctionSymbol(smLib, "SmcInteractDone"); + SmcSaveYourselfDone = (SmcSaveYourselfDoneFn)PR_FindFunctionSymbol(smLib, "SmcSaveYourselfDone"); + SmcInteractRequest = (SmcInteractRequestFn)PR_FindFunctionSymbol(smLib, "SmcInteractRequest"); + SmcCloseConnection = (SmcCloseConnectionFn)PR_FindFunctionSymbol(smLib, "SmcCloseConnection"); + SmcOpenConnection = (SmcOpenConnectionFn)PR_FindFunctionSymbol(smLib, "SmcOpenConnection"); + SmcSetProperties = (SmcSetPropertiesFn)PR_FindFunctionSymbol(smLib, "SmcSetProperties"); + if (!SmcInteractDone || !SmcSaveYourselfDone || !SmcInteractRequest || + !SmcCloseConnection || !SmcOpenConnection || !SmcSetProperties) { + PR_UnloadLibrary(iceLib); + PR_UnloadLibrary(smLib); + return NS_OK; + } + + ice_init(); + + // all callbacks are mandatory in libSM 1.0, so listen even if we don't care. + unsigned long mask = SmcSaveYourselfProcMask | SmcDieProcMask | + SmcSaveCompleteProcMask | SmcShutdownCancelledProcMask; + + SmcCallbacks callbacks; + callbacks.save_yourself.callback = nsNativeAppSupportUnix::SaveYourselfCB; + callbacks.save_yourself.client_data = static_cast<SmPointer>(this); + + callbacks.die.callback = nsNativeAppSupportUnix::DieCB; + callbacks.die.client_data = static_cast<SmPointer>(this); + + callbacks.save_complete.callback = nsNativeAppSupportUnix::SaveCompleteCB; + callbacks.save_complete.client_data = nullptr; + + callbacks.shutdown_cancelled.callback = + nsNativeAppSupportUnix::ShutdownCancelledCB; + callbacks.shutdown_cancelled.client_data = static_cast<SmPointer>(this); + + char errbuf[256]; + mSessionConnection = SmcOpenConnection(nullptr, this, SmProtoMajor, + SmProtoMinor, mask, &callbacks, + prev_client_id.get(), &client_id, + sizeof(errbuf), errbuf); + } + + if (!mSessionConnection) { + return NS_OK; + } + + LogModule::Init(); // need to make sure initialized before SetClientState + if (prev_client_id.IsEmpty() || + (client_id && !prev_client_id.Equals(client_id))) { + SetClientState(STATE_REGISTERING); + } else { + SetClientState(STATE_IDLE); + } + + gdk_x11_set_sm_client_id(client_id); + + // Set SM Properties + // SmCloneCommand, SmProgram, SmRestartCommand, SmUserID are required + // properties so must be set, and must have a sensible fallback value. + + // Determine executable path to use for XSMP session restore + + // Is there a request to suppress default binary launcher? + nsAutoCString path(getenv("MOZ_APP_LAUNCHER")); + + if (path.IsEmpty()) { + NS_ASSERTION(gDirServiceProvider, "gDirServiceProvider is NULL! This shouldn't happen!"); + nsCOMPtr<nsIFile> executablePath; + nsresult rv; + + bool dummy; + rv = gDirServiceProvider->GetFile(XRE_EXECUTABLE_FILE, &dummy, getter_AddRefs(executablePath)); + + if (NS_SUCCEEDED(rv)) { + // Strip off the -bin suffix to get the shell script we should run; this is what Breakpad does + nsAutoCString leafName; + rv = executablePath->GetNativeLeafName(leafName); + if (NS_SUCCEEDED(rv) && StringEndsWith(leafName, NS_LITERAL_CSTRING("-bin"))) { + leafName.SetLength(leafName.Length() - strlen("-bin")); + executablePath->SetNativeLeafName(leafName); + } + + executablePath->GetNativePath(path); + } + } + + if (path.IsEmpty()) { + // can't determine executable path. Best fallback is name from + // application.ini but it might not resolve to the same executable at + // launch time. + path = gAppData->name; // will always be set + ToLowerCase(path); + MOZ_LOG(sMozSMLog, LogLevel::Warning, + ("Could not determine executable path. Falling back to %s.", path.get())); + } + + SmProp propRestart, propClone, propProgram, propUser, *props[4]; + SmPropValue valsRestart[3], valsClone[1], valsProgram[1], valsUser[1]; + int n = 0; + + NS_NAMED_LITERAL_CSTRING(kClientIDParam, "--sm-client-id"); + + SetSMValue(valsRestart[0], path); + SetSMValue(valsRestart[1], kClientIDParam); + SetSMValue(valsRestart[2], nsDependentCString(client_id)); + SetSMProperty(propRestart, SmRestartCommand, SmLISTofARRAY8, 3, valsRestart); + props[n++] = &propRestart; + + SetSMValue(valsClone[0], path); + SetSMProperty(propClone, SmCloneCommand, SmLISTofARRAY8, 1, valsClone); + props[n++] = &propClone; + + nsAutoCString appName(gAppData->name); // will always be set + ToLowerCase(appName); + + SetSMValue(valsProgram[0], appName); + SetSMProperty(propProgram, SmProgram, SmARRAY8, 1, valsProgram); + props[n++] = &propProgram; + + nsAutoCString userName; // username that started the program + struct passwd* pw = getpwuid(getuid()); + if (pw && pw->pw_name) { + userName = pw->pw_name; + } else { + userName = NS_LITERAL_CSTRING("nobody"); + MOZ_LOG(sMozSMLog, LogLevel::Warning, + ("Could not determine user-name. Falling back to %s.", userName.get())); + } + + SetSMValue(valsUser[0], userName); + SetSMProperty(propUser, SmUserID, SmARRAY8, 1, valsUser); + props[n++] = &propUser; + + SmcSetProperties(mSessionConnection, n, props); + + g_free(client_id); +#endif /* MOZ_X11 */ + + return NS_OK; +} + +NS_IMETHODIMP +nsNativeAppSupportUnix::Stop(bool *aResult) +{ + NS_ENSURE_ARG(aResult); + *aResult = true; + return NS_OK; +} + +NS_IMETHODIMP +nsNativeAppSupportUnix::Enable() +{ + return NS_OK; +} + +nsresult +NS_CreateNativeAppSupport(nsINativeAppSupport **aResult) +{ + nsNativeAppSupportBase* native = new nsNativeAppSupportUnix(); + if (!native) + return NS_ERROR_OUT_OF_MEMORY; + + *aResult = native; + NS_ADDREF(*aResult); + + return NS_OK; +} |