зеркало из https://github.com/mozilla/gecko-dev.git
709 строки
22 KiB
C++
709 строки
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 "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;
|
|
}
|