зеркало из https://github.com/mozilla/gecko-dev.git
Bug 694570 (2/2) - Stop using libgnome and libgnomeui on Linux. r=acomminos
(Based on original patch by Chris Coulson.) Replace session management through libgnome with direct use of libSM and libICE. This allows xsmp session restore on gtk2 and gtk3 builds. Changes in behaviour: - It now only sends the "session-save" notification when the save style is SmSaveLocal or SmSaveBoth. Saving internal state with a save style of SmSaveGlobal is actually incorrect. This means that Firefox now distinguishes between a normal session exit and a session exit with session saving enabled. - As "quit-application-requested" might pop up a dialog, it only does this if the interact style is not SmInteractStyleNone - "quit-application-requested" is only sent after sending SmcInteractRequest and receiving an interact message. - It defers closing the connection to the session manager until the destructor, i.e after the "web-workers-shutdown" async shutdown phase completes. This means that firefox shouldn't be killed too early and lose data (*). * It still might be killed prematurely if it takes too long to quit and the session manager decides to timeout, but that's another story.
This commit is contained in:
Родитель
0db0cdd5a1
Коммит
8bfcce62d0
|
@ -35,6 +35,13 @@
|
|||
#ifdef MOZ_X11
|
||||
#include <gdk/gdkx.h>
|
||||
#include <X11/Xatom.h>
|
||||
#include <X11/Xlib.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
|
||||
|
@ -47,95 +54,399 @@
|
|||
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."
|
||||
|
||||
typedef struct _GnomeProgram GnomeProgram;
|
||||
typedef struct _GnomeModuleInfo GnomeModuleInfo;
|
||||
typedef struct _GnomeClient GnomeClient;
|
||||
#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 enum {
|
||||
GNOME_SAVE_GLOBAL,
|
||||
GNOME_SAVE_LOCAL,
|
||||
GNOME_SAVE_BOTH
|
||||
} GnomeSaveStyle;
|
||||
typedef IceIOErrorHandler (*IceSetIOErrorHandlerFn) (IceIOErrorHandler);
|
||||
typedef int (*IceAddConnectionWatchFn) (IceWatchProc, IcePointer);
|
||||
typedef int (*IceConnectionNumberFn) (IceConn);
|
||||
typedef IceProcessMessagesStatus (*IceProcessMessagesFn) (IceConn, IceReplyWaitInfo*, Bool*);
|
||||
typedef IcePointer (*IceGetConnectionContextFn) (IceConn);
|
||||
|
||||
typedef enum {
|
||||
GNOME_INTERACT_NONE,
|
||||
GNOME_INTERACT_ERRORS,
|
||||
GNOME_INTERACT_ANY
|
||||
} GnomeInteractStyle;
|
||||
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**);
|
||||
|
||||
typedef enum {
|
||||
GNOME_DIALOG_ERROR,
|
||||
GNOME_DIALOG_NORMAL
|
||||
} GnomeDialogType;
|
||||
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;
|
||||
|
||||
#if defined(MOZ_X11) && (MOZ_WIDGET_GTK == 2)
|
||||
typedef GnomeProgram * (*_gnome_program_init_fn)(const char *, const char *,
|
||||
const GnomeModuleInfo *, int,
|
||||
char **, const char *, ...);
|
||||
typedef GnomeProgram * (*_gnome_program_get_fn)(void);
|
||||
typedef const GnomeModuleInfo * (*_libgnomeui_module_info_get_fn)();
|
||||
typedef GnomeClient * (*_gnome_master_client_fn)(void);
|
||||
typedef void (*_gnome_client_set_restart_command_fn)(GnomeClient*, gint, gchar*[]);
|
||||
#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
|
||||
|
||||
static _gnome_client_set_restart_command_fn gnome_client_set_restart_command;
|
||||
#endif
|
||||
enum ClientState {
|
||||
STATE_DISCONNECTED,
|
||||
STATE_REGISTERING,
|
||||
STATE_IDLE,
|
||||
STATE_INTERACTING,
|
||||
STATE_SHUTDOWN_CANCELLED
|
||||
};
|
||||
|
||||
gboolean save_yourself_cb(GnomeClient *client, gint phase,
|
||||
GnomeSaveStyle style, gboolean shutdown,
|
||||
GnomeInteractStyle interact, gboolean fast,
|
||||
gpointer user_data)
|
||||
{
|
||||
nsCOMPtr<nsIObserverService> obsServ =
|
||||
mozilla::services::GetObserverService();
|
||||
static const char *gClientStateTable[] = {
|
||||
"DISCONNECTED",
|
||||
"REGISTERING",
|
||||
"IDLE",
|
||||
"INTERACTING",
|
||||
"SHUTDOWN_CANCELLED"
|
||||
};
|
||||
|
||||
nsCOMPtr<nsISupportsPRBool> didSaveSession =
|
||||
do_CreateInstance(NS_SUPPORTS_PRBOOL_CONTRACTID);
|
||||
|
||||
if (!obsServ || !didSaveSession)
|
||||
return TRUE; // OOM
|
||||
|
||||
// Notify observers to save the session state
|
||||
didSaveSession->SetData(false);
|
||||
obsServ->NotifyObservers(didSaveSession, "session-save", nullptr);
|
||||
|
||||
bool status;
|
||||
didSaveSession->GetData(&status);
|
||||
|
||||
// If there was no session saved and the save_yourself request is
|
||||
// caused by upcoming shutdown we like to prepare for it
|
||||
if (!status && shutdown) {
|
||||
nsCOMPtr<nsISupportsPRBool> cancelQuit =
|
||||
do_CreateInstance(NS_SUPPORTS_PRBOOL_CONTRACTID);
|
||||
|
||||
cancelQuit->SetData(false);
|
||||
obsServ->NotifyObservers(cancelQuit, "quit-application-requested", nullptr);
|
||||
|
||||
bool abortQuit;
|
||||
cancelQuit->GetData(&abortQuit);
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
void die_cb(GnomeClient *client, gpointer user_data)
|
||||
{
|
||||
nsCOMPtr<nsIAppStartup> appService =
|
||||
do_GetService("@mozilla.org/toolkit/app-startup;1");
|
||||
|
||||
if (appService)
|
||||
appService->Quit(nsIAppStartup::eForceQuit);
|
||||
}
|
||||
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)
|
||||
{
|
||||
|
@ -169,79 +480,132 @@ nsNativeAppSupportUnix::Start(bool *aRetVal)
|
|||
|
||||
*aRetVal = true;
|
||||
|
||||
#if defined(MOZ_X11) && (MOZ_WIDGET_GTK == 2)
|
||||
#ifdef MOZ_X11
|
||||
gboolean sm_disable = FALSE;
|
||||
if (!getenv("SESSION_MANAGER")) {
|
||||
sm_disable = TRUE;
|
||||
}
|
||||
|
||||
PRLibrary *gnomeuiLib = PR_LoadLibrary("libgnomeui-2.so.0");
|
||||
if (!gnomeuiLib)
|
||||
return NS_OK;
|
||||
nsAutoCString prev_client_id;
|
||||
|
||||
PRLibrary *gnomeLib = PR_LoadLibrary("libgnome-2.so.0");
|
||||
if (!gnomeLib) {
|
||||
PR_UnloadLibrary(gnomeuiLib);
|
||||
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;
|
||||
}
|
||||
|
||||
_gnome_program_init_fn gnome_program_init =
|
||||
(_gnome_program_init_fn)PR_FindFunctionSymbol(gnomeLib, "gnome_program_init");
|
||||
_gnome_program_get_fn gnome_program_get =
|
||||
(_gnome_program_get_fn)PR_FindFunctionSymbol(gnomeLib, "gnome_program_get");
|
||||
_libgnomeui_module_info_get_fn libgnomeui_module_info_get = (_libgnomeui_module_info_get_fn)PR_FindFunctionSymbol(gnomeuiLib, "libgnomeui_module_info_get");
|
||||
if (!gnome_program_init || !gnome_program_get || !libgnomeui_module_info_get) {
|
||||
PR_UnloadLibrary(gnomeuiLib);
|
||||
PR_UnloadLibrary(gnomeLib);
|
||||
PRLibrary *smLib = PR_LoadLibrary("libSM.so.6");
|
||||
if (!smLib) {
|
||||
PR_UnloadLibrary(iceLib);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
#endif /* MOZ_X11 && (MOZ_WIDGET_GTK == 2) */
|
||||
|
||||
#ifdef ACCESSIBILITY
|
||||
// We will load gail, atk-bridge by ourself later
|
||||
// We can't run atk-bridge init here, because gail get the control
|
||||
// Set GNOME_ACCESSIBILITY to 0 can avoid this
|
||||
static const char *accEnv = "GNOME_ACCESSIBILITY";
|
||||
const char *accOldValue = getenv(accEnv);
|
||||
setenv(accEnv, "0", 1);
|
||||
#endif
|
||||
|
||||
#if defined(MOZ_X11) && (MOZ_WIDGET_GTK == 2)
|
||||
if (!gnome_program_get()) {
|
||||
gnome_program_init("Gecko", "1.0", libgnomeui_module_info_get(),
|
||||
gArgc, gArgv, nullptr);
|
||||
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;
|
||||
}
|
||||
#endif /* MOZ_X11 && (MOZ_WIDGET_GTK == 2) */
|
||||
|
||||
#ifdef ACCESSIBILITY
|
||||
if (accOldValue) {
|
||||
setenv(accEnv, accOldValue, 1);
|
||||
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 {
|
||||
unsetenv(accEnv);
|
||||
SetClientState(STATE_IDLE);
|
||||
}
|
||||
#endif
|
||||
|
||||
// Careful! These libraries cannot be unloaded after this point because
|
||||
// gnome_program_init causes atexit handlers to be registered. Strange
|
||||
// crashes will occur if these libraries are unloaded.
|
||||
gdk_x11_set_sm_client_id(client_id);
|
||||
|
||||
// TODO GTK3 - see Bug 694570 - Stop using libgnome and libgnomeui on Linux
|
||||
#if defined(MOZ_X11) && (MOZ_WIDGET_GTK == 2)
|
||||
gnome_client_set_restart_command = (_gnome_client_set_restart_command_fn)
|
||||
PR_FindFunctionSymbol(gnomeuiLib, "gnome_client_set_restart_command");
|
||||
// Set SM Properties
|
||||
// SmCloneCommand, SmProgram, SmRestartCommand, SmUserID are required
|
||||
// properties so must be set, and must have a sensible fallback value.
|
||||
|
||||
_gnome_master_client_fn gnome_master_client = (_gnome_master_client_fn)
|
||||
PR_FindFunctionSymbol(gnomeuiLib, "gnome_master_client");
|
||||
|
||||
GnomeClient *client = gnome_master_client();
|
||||
g_signal_connect(client, "save-yourself", G_CALLBACK(save_yourself_cb), nullptr);
|
||||
g_signal_connect(client, "die", G_CALLBACK(die_cb), nullptr);
|
||||
|
||||
// Set the correct/requested restart command in any case.
|
||||
// Determine executable path to use for XSMP session restore
|
||||
|
||||
// Is there a request to suppress default binary launcher?
|
||||
nsAutoCString path;
|
||||
char* argv1 = getenv("MOZ_APP_LAUNCHER");
|
||||
nsAutoCString path(getenv("MOZ_APP_LAUNCHER"));
|
||||
|
||||
if(!argv1) {
|
||||
// Tell the desktop the command for restarting us so that we can be part of XSMP session restore
|
||||
if (path.IsEmpty()) {
|
||||
NS_ASSERTION(gDirServiceProvider, "gDirServiceProvider is NULL! This shouldn't happen!");
|
||||
nsCOMPtr<nsIFile> executablePath;
|
||||
nsresult rv;
|
||||
|
@ -259,14 +623,60 @@ nsNativeAppSupportUnix::Start(bool *aRetVal)
|
|||
}
|
||||
|
||||
executablePath->GetNativePath(path);
|
||||
argv1 = (char*)(path.get());
|
||||
}
|
||||
}
|
||||
|
||||
if (argv1) {
|
||||
gnome_client_set_restart_command(client, 1, &argv1);
|
||||
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()));
|
||||
}
|
||||
#endif /* MOZ_X11 && (MOZ_WIDGET_GTK == 2) */
|
||||
|
||||
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;
|
||||
}
|
||||
|
|
|
@ -5,9 +5,14 @@
|
|||
#ifndef GDKX_WRAPPER_H
|
||||
#define GDKX_WRAPPER_H
|
||||
|
||||
#include <gtk/gtkversion.h>
|
||||
|
||||
#define gdk_x11_window_foreign_new_for_display gdk_x11_window_foreign_new_for_display_
|
||||
#define gdk_x11_window_lookup_for_display gdk_x11_window_lookup_for_display_
|
||||
#define gdk_x11_window_get_xid gdk_x11_window_get_xid_
|
||||
#if !GTK_CHECK_VERSION(2,24,0)
|
||||
#define gdk_x11_set_sm_client_id gdk_x11_set_sm_client_id_
|
||||
#endif
|
||||
#include_next <gdk/gdkx.h>
|
||||
#undef gdk_x11_window_foreign_new_for_display
|
||||
#undef gdk_x11_window_lookup_for_display
|
||||
|
@ -35,4 +40,12 @@ gdk_x11_window_get_xid(GdkWindow *window)
|
|||
#define GDK_IS_X11_DISPLAY(a) (true)
|
||||
#endif
|
||||
|
||||
#if !GTK_CHECK_VERSION(2,24,0)
|
||||
#undef gdk_x11_set_sm_client_id
|
||||
static inline void
|
||||
gdk_x11_set_sm_client_id (const gchar *sm_client_id)
|
||||
{
|
||||
gdk_set_sm_client_id(sm_client_id);
|
||||
}
|
||||
#endif
|
||||
#endif /* GDKX_WRAPPER_H */
|
||||
|
|
|
@ -133,6 +133,7 @@ STUB(gdk_x11_window_foreign_new_for_display)
|
|||
STUB(gdk_x11_window_lookup_for_display)
|
||||
STUB(gdk_x11_window_set_user_time)
|
||||
STUB(gdk_x11_xatom_to_atom)
|
||||
STUB(gdk_x11_set_sm_client_id)
|
||||
STUB(gtk_accel_label_new)
|
||||
STUB(gtk_alignment_get_type)
|
||||
STUB(gtk_alignment_new)
|
||||
|
|
Загрузка…
Ссылка в новой задаче