summaryrefslogtreecommitdiffstats
path: root/toolkit/xre/nsNativeAppSupportUnix.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'toolkit/xre/nsNativeAppSupportUnix.cpp')
-rw-r--r--toolkit/xre/nsNativeAppSupportUnix.cpp708
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;
+}