mirror of
https://github.com/mozilla/gecko-dev.git
synced 2025-01-27 07:34:20 +00:00
5dc21d568c
The inclusions were removed with the following very crude script and the resulting breakage was fixed up by hand. The manual fixups did either revert the changes done by the script, replace a generic header with a more specific one or replace a header with a forward declaration. find . -name "*.idl" | grep -v web-platform | grep -v third_party | while read path; do interfaces=$(grep "^\(class\|interface\).*:.*" "$path" | cut -d' ' -f2) if [ -n "$interfaces" ]; then if [[ "$interfaces" == *$'\n'* ]]; then regexp="\(" for i in $interfaces; do regexp="$regexp$i\|"; done regexp="${regexp%%\\\|}\)" else regexp="$interfaces" fi interface=$(basename "$path") rg -l "#include.*${interface%%.idl}.h" . | while read path2; do hits=$(grep -v "#include.*${interface%%.idl}.h" "$path2" | grep -c "$regexp" ) if [ $hits -eq 0 ]; then echo "Removing ${interface} from ${path2}" grep -v "#include.*${interface%%.idl}.h" "$path2" > "$path2".tmp mv -f "$path2".tmp "$path2" fi done fi done Differential Revision: https://phabricator.services.mozilla.com/D55444 --HG-- extra : moz-landing-system : lando
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) {
|
|
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;
|
|
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
|
|
|
|
*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, 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::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;
|
|
}
|