forked from mirrors/gecko-dev
		
	
		
			
				
	
	
		
			657 lines
		
	
	
	
		
			22 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			657 lines
		
	
	
	
		
			22 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
/* -*- 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 "nsPIDOMWindow.h"
 | 
						|
#include "nsIWidget.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) override;
 | 
						|
  NS_IMETHOD Enable() override;
 | 
						|
 | 
						|
 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("nsNativeAppSupportUnix::DoInteract", 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) {
 | 
						|
      bool userAllowedQuit = true;
 | 
						|
      appService->Quit(nsIAppStartup::eForceQuit, &userAllowedQuit);
 | 
						|
    }
 | 
						|
  } 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;
 | 
						|
  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) {
 | 
						|
    bool userAllowedQuit = false;
 | 
						|
    appService->Quit(nsIAppStartup::eForceQuit, &userAllowedQuit);
 | 
						|
  }
 | 
						|
  // 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
 | 
						|
 | 
						|
  *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(
 | 
						|
      gArgc, gArgv);  // 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, "-bin"_ns)) {
 | 
						|
        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;
 | 
						|
 | 
						|
  constexpr auto kClientIDParam = "--sm-client-id"_ns;
 | 
						|
 | 
						|
  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 = "nobody"_ns;
 | 
						|
    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::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;
 | 
						|
}
 |