summaryrefslogtreecommitdiffstats
path: root/src/libs/xpcom18a4/ipc/ipcd/client
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-06 03:01:46 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-06 03:01:46 +0000
commitf8fe689a81f906d1b91bb3220acde2a4ecb14c5b (patch)
tree26484e9d7e2c67806c2d1760196ff01aaa858e8c /src/libs/xpcom18a4/ipc/ipcd/client
parentInitial commit. (diff)
downloadvirtualbox-f8fe689a81f906d1b91bb3220acde2a4ecb14c5b.tar.xz
virtualbox-f8fe689a81f906d1b91bb3220acde2a4ecb14c5b.zip
Adding upstream version 6.0.4-dfsg.upstream/6.0.4-dfsgupstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/libs/xpcom18a4/ipc/ipcd/client')
-rw-r--r--src/libs/xpcom18a4/ipc/ipcd/client/Makefile.kup0
-rw-r--r--src/libs/xpcom18a4/ipc/ipcd/client/public/.cvsignore1
-rw-r--r--src/libs/xpcom18a4/ipc/ipcd/client/public/Makefile.in59
-rw-r--r--src/libs/xpcom18a4/ipc/ipcd/client/public/ipcCID.h53
-rw-r--r--src/libs/xpcom18a4/ipc/ipcd/client/public/ipcIClientObserver.idl51
-rw-r--r--src/libs/xpcom18a4/ipc/ipcd/client/public/ipcIMessageObserver.idl64
-rw-r--r--src/libs/xpcom18a4/ipc/ipcd/client/public/ipcIService.idl228
-rw-r--r--src/libs/xpcom18a4/ipc/ipcd/client/public/ipcdclient.h326
-rw-r--r--src/libs/xpcom18a4/ipc/ipcd/client/src/.cvsignore1
-rw-r--r--src/libs/xpcom18a4/ipc/ipcd/client/src/Makefile.in101
-rw-r--r--src/libs/xpcom18a4/ipc/ipcd/client/src/Makefile.kup0
-rw-r--r--src/libs/xpcom18a4/ipc/ipcd/client/src/ipcConnection.h147
-rw-r--r--src/libs/xpcom18a4/ipc/ipcd/client/src/ipcConnectionStub.cpp70
-rw-r--r--src/libs/xpcom18a4/ipc/ipcd/client/src/ipcConnectionUnix.cpp615
-rw-r--r--src/libs/xpcom18a4/ipc/ipcd/client/src/ipcConnectionWin.cpp332
-rw-r--r--src/libs/xpcom18a4/ipc/ipcd/client/src/ipcModuleFactory.cpp196
-rw-r--r--src/libs/xpcom18a4/ipc/ipcd/client/src/ipcService.cpp120
-rw-r--r--src/libs/xpcom18a4/ipc/ipcd/client/src/ipcService.h51
-rw-r--r--src/libs/xpcom18a4/ipc/ipcd/client/src/ipcdclient.cpp1490
19 files changed, 3905 insertions, 0 deletions
diff --git a/src/libs/xpcom18a4/ipc/ipcd/client/Makefile.kup b/src/libs/xpcom18a4/ipc/ipcd/client/Makefile.kup
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/src/libs/xpcom18a4/ipc/ipcd/client/Makefile.kup
diff --git a/src/libs/xpcom18a4/ipc/ipcd/client/public/.cvsignore b/src/libs/xpcom18a4/ipc/ipcd/client/public/.cvsignore
new file mode 100644
index 00000000..f3c7a7c5
--- /dev/null
+++ b/src/libs/xpcom18a4/ipc/ipcd/client/public/.cvsignore
@@ -0,0 +1 @@
+Makefile
diff --git a/src/libs/xpcom18a4/ipc/ipcd/client/public/Makefile.in b/src/libs/xpcom18a4/ipc/ipcd/client/public/Makefile.in
new file mode 100644
index 00000000..896e68b8
--- /dev/null
+++ b/src/libs/xpcom18a4/ipc/ipcd/client/public/Makefile.in
@@ -0,0 +1,59 @@
+# vim: noexpandtab ts=4 sw=4
+# ***** BEGIN LICENSE BLOCK *****
+# Version: MPL 1.1/GPL 2.0/LGPL 2.1
+#
+# The contents of this file are subject to the Mozilla Public License Version
+# 1.1 (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+# http://www.mozilla.org/MPL/
+#
+# Software distributed under the License is distributed on an "AS IS" basis,
+# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+# for the specific language governing rights and limitations under the
+# License.
+#
+# The Original Code is Mozilla IPC.
+#
+# The Initial Developer of the Original Code is
+# Netscape Communications Corporation.
+# Portions created by the Initial Developer are Copyright (C) 2002
+# the Initial Developer. All Rights Reserved.
+#
+# Contributor(s):
+# Darin Fisher <darin@netscape.com>
+#
+# Alternatively, the contents of this file may be used under the terms of
+# either the GNU General Public License Version 2 or later (the "GPL"), or
+# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+# in which case the provisions of the GPL or the LGPL are applicable instead
+# of those above. If you wish to allow use of your version of this file only
+# under the terms of either the GPL or the LGPL, and not to allow others to
+# use your version of this file under the terms of the MPL, indicate your
+# decision by deleting the provisions above and replace them with the notice
+# and other provisions required by the GPL or the LGPL. If you do not delete
+# the provisions above, a recipient may use your version of this file under
+# the terms of any one of the MPL, the GPL or the LGPL.
+#
+# ***** END LICENSE BLOCK *****
+
+DEPTH = ../../../..
+topsrcdir = @top_srcdir@
+srcdir = @srcdir@
+VPATH = @srcdir@
+
+include $(DEPTH)/config/autoconf.mk
+
+MODULE = ipcd
+
+EXPORTS = \
+ ipcdclient.h \
+ ipcCID.h \
+ $(NULL)
+
+XPIDLSRCS = \
+ ipcIService.idl \
+ ipcIMessageObserver.idl \
+ ipcIClientObserver.idl \
+ $(NULL)
+
+include $(topsrcdir)/config/rules.mk
diff --git a/src/libs/xpcom18a4/ipc/ipcd/client/public/ipcCID.h b/src/libs/xpcom18a4/ipc/ipcd/client/public/ipcCID.h
new file mode 100644
index 00000000..4863ff0d
--- /dev/null
+++ b/src/libs/xpcom18a4/ipc/ipcd/client/public/ipcCID.h
@@ -0,0 +1,53 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Mozilla IPC.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 2002
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Darin Fisher <darin@netscape.com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#ifndef ipcCID_h__
+#define ipcCID_h__
+
+#define IPC_SERVICE_CLASSNAME \
+ "ipcService"
+#define IPC_SERVICE_CONTRACTID \
+ "@mozilla.org/ipc/service;1"
+#define IPC_SERVICE_CID \
+{ /* 9f12676a-5168-4a08-beb8-edf8a593a1ca */ \
+ 0x9f12676a, \
+ 0x5168, \
+ 0x4a08, \
+ {0xbe, 0xb8, 0xed, 0xf8, 0xa5, 0x93, 0xa1, 0xca} \
+}
+
+#endif // !ipcCID_h__
diff --git a/src/libs/xpcom18a4/ipc/ipcd/client/public/ipcIClientObserver.idl b/src/libs/xpcom18a4/ipc/ipcd/client/public/ipcIClientObserver.idl
new file mode 100644
index 00000000..c72d0909
--- /dev/null
+++ b/src/libs/xpcom18a4/ipc/ipcd/client/public/ipcIClientObserver.idl
@@ -0,0 +1,51 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Mozilla IPC.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 2002
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Darin Fisher <darin@netscape.com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#include "nsISupports.idl"
+
+/**
+ * ipcIClientObserver
+ */
+[scriptable, uuid(42283079-030c-4b13-b069-a08b7ad5eab2)]
+interface ipcIClientObserver : nsISupports
+{
+ const unsigned long CLIENT_UP = 1;
+ const unsigned long CLIENT_DOWN = 2;
+
+ void onClientStateChange(in unsigned long aClientID,
+ in unsigned long aClientState);
+};
diff --git a/src/libs/xpcom18a4/ipc/ipcd/client/public/ipcIMessageObserver.idl b/src/libs/xpcom18a4/ipc/ipcd/client/public/ipcIMessageObserver.idl
new file mode 100644
index 00000000..db24b1f2
--- /dev/null
+++ b/src/libs/xpcom18a4/ipc/ipcd/client/public/ipcIMessageObserver.idl
@@ -0,0 +1,64 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Mozilla IPC.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 2002
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Darin Fisher <darin@netscape.com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#include "nsISupports.idl"
+
+/**
+ * ipcIMessageObserver
+ */
+[scriptable, uuid(e40a4a3c-2dc1-470e-ab7f-5675fe1f1384)]
+interface ipcIMessageObserver : nsISupports
+{
+ /**
+ * @param aSenderID
+ * the client id of the sender of this message. if sent by the
+ * daemon (or a deamon module), then this will have a value of 0.
+ * @param aTarget
+ * the target of the message, corresponding to the target this
+ * observer was registered under. this parameter is passed to allow
+ * an observer instance to receive messages for more than one target.
+ * @param aData
+ * the data of the message.
+ * @param aDataLen
+ * the data length of the message.
+ */
+ void onMessageAvailable(in unsigned long aSenderID,
+ in nsIDRef aTarget,
+ [array, const, size_is(aDataLen)]
+ in octet aData,
+ in unsigned long aDataLen);
+};
diff --git a/src/libs/xpcom18a4/ipc/ipcd/client/public/ipcIService.idl b/src/libs/xpcom18a4/ipc/ipcd/client/public/ipcIService.idl
new file mode 100644
index 00000000..671b557d
--- /dev/null
+++ b/src/libs/xpcom18a4/ipc/ipcd/client/public/ipcIService.idl
@@ -0,0 +1,228 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Mozilla IPC.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 2002
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Darin Fisher <darin@netscape.com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#include "nsISupports.idl"
+
+interface ipcIMessageObserver;
+interface ipcIClientObserver;
+
+/**
+ * ipcIService
+ *
+ * the IPC service provides the means to communicate with an external IPC
+ * daemon and/or other mozilla-based applications on the same physical system.
+ * the IPC daemon hosts modules (some builtin and others dynamically loaded)
+ * with which applications may interact.
+ *
+ * at application startup, the IPC service will attempt to establish a
+ * connection with the IPC daemon. the IPC daemon will be automatically
+ * started if necessary. when a connection has been established, the IPC
+ * service will enumerate the "ipc-startup-category" and broadcast an
+ * "ipc-startup" notification using the observer service.
+ *
+ * when the connection to the IPC daemon is closed, an "ipc-shutdown"
+ * notification will be broadcast.
+ *
+ * each client has a name. the client name need not be unique across all
+ * clients, but it is usually good if it is. the IPC service does not require
+ * unique names. instead, the IPC daemon assigns each client a unique ID that
+ * is good for the current "session." clients can query other clients by name
+ * or by ID. the IPC service supports forwarding messages from one client to
+ * another via the IPC daemon.
+ *
+ * for performance reasons, this system should not be used to transfer large
+ * amounts of data. instead, applications may choose to utilize shared memory,
+ * and rely on the IPC service for synchronization and small message transfer
+ * only.
+ */
+[scriptable, uuid(53d3e3a7-528f-4b09-9eab-9416272568c0)]
+interface ipcIService : nsISupports
+{
+ /**************************************************************************
+ * properties of this process
+ */
+
+ /**
+ * returns the "client ID" assigned to this process by the IPC daemon.
+ *
+ * @throws NS_ERROR_NOT_AVAILABLE if no connection to the IPC daemon.
+ */
+ readonly attribute unsigned long ID;
+
+ /**
+ * this process can appear under several client names. use the following
+ * methods to add or remove names for this process.
+ *
+ * for example, the mozilla browser might have the primary name "mozilla",
+ * but it could also register itself under the names "browser", "mail",
+ * "news", "addrbook", etc. other IPC clients can then query the IPC
+ * daemon for the client named "mail" in order to talk with a mail program.
+ *
+ * XXX An IPC client name resembles a XPCOM contract ID.
+ */
+ void addName(in string aName);
+ void removeName(in string aName);
+
+ /**
+ * add a new observer of client status change notifications.
+ */
+ void addClientObserver(in ipcIClientObserver aObserver);
+
+ /**
+ * remove an observer of client status change notifications.
+ */
+ void removeClientObserver(in ipcIClientObserver aObserver);
+
+ /**************************************************************************
+ * client query methods
+ */
+
+ /**
+ * resolve the given client name to the id of a connected client. this
+ * involves a round trip to the daemon, and as a result the calling thread
+ * may block on this function call while waiting for the daemon to respond.
+ */
+ unsigned long resolveClientName(in string aName);
+
+
+ /**
+ * tests whether a particular client is connected to the IPC daemon.
+ */
+ boolean clientExists(in unsigned long aClientID);
+
+
+ // XXX need other functions to enumerate clients, clients implementing targets, etc.
+ // enumerator getClients();
+ // enumerator getClientsSupportingTarget(in nsIDRef aTarget);
+ // enumerator getClientNames(in unsigned long aClientID);
+ // enumerator getClientTargets(in unsigned long aClientID);
+
+
+ /**************************************************************************
+ * message methods
+ */
+
+ /**
+ * set a message observer for a particular message target.
+ *
+ * @param aTarget
+ * the message target being observed. any existing observer will
+ * be replaced.
+ * @param aObserver
+ * the message observer to receive incoming messages for the
+ * specified target. pass null to remove the existing observer.
+ * @param aOnCurrentThread
+ * if true, then the message observer will be called on the same
+ * thread that calls defineTarget. otherwise, aObserver will be
+ * called on a background thread.
+ */
+ void defineTarget(in nsIDRef aTarget,
+ in ipcIMessageObserver aObserver,
+ in boolean aOnCurrentThread);
+
+ /**
+ * send message asynchronously to a client or a module in the IPC daemon.
+ * there is no guarantee that the message will be delivered.
+ *
+ * @param aClientID
+ * the client ID of the foreign application that should receive this
+ * message. pass 0 to send a message to a module in the IPC daemon.
+ * @param aTarget
+ * the target of the message. if aClientID is 0, then this is the
+ * ID of the daemon module that should receive this message.
+ * @param aData
+ * the message data.
+ * @param aDataLen
+ * the message length.
+ */
+ void sendMessage(in unsigned long aReceiverID,
+ in nsIDRef aTarget,
+ [array, const, size_is(aDataLen)]
+ in octet aData,
+ in unsigned long aDataLen);
+
+ /**
+ * block the calling thread until a matching message is received.
+ *
+ * @param aSenderID
+ * pass 0 to wait for a message from the daemon. pass PR_UINT32_MAX
+ * to wait for a message from any source. otherwise, pass a client
+ * id to wait for a message from that particular client.
+ * @param aTarget
+ * wait for a message to be delivered to this target.
+ * @param aObserver
+ * this observer's OnMessageAvailable method is called when a
+ * matching message is available. pass null to use the default
+ * observer associated with aTarget.
+ * @param aTimeout
+ * indicates maximum length of time in milliseconds that this
+ * function may block the calling thread.
+ *
+ * @throws IPC_ERROR_WOULD_BLOCK if the timeout expires.
+ *
+ * the observer's OnMessageAvailable method may throw IPC_WAIT_NEXT_MESSAGE
+ * to indicate that it does not wish to handle the message that it was
+ * given, and that it will wait to be called with the next message. this
+ * enables the observer to keep messages in the queue that do not match the
+ * desired message. messages that remain in the queue will be dispatched
+ * asynchronously to the default message handler after waitMessage finishes.
+ *
+ * NOTE: this function may hang the calling thread until a matching message
+ * is received, so use it with caution.
+ */
+ void waitMessage(in unsigned long aSenderID,
+ in nsIDRef aTarget,
+ in ipcIMessageObserver aObserver,
+ in unsigned long aTimeout);
+
+ /**
+ * Call this method to disable the default message observer for a target.
+ */
+ void disableMessageObserver(in nsIDRef aTarget);
+
+ /**
+ * Call this method to re-enable the default message observer for a target.
+ */
+ void enableMessageObserver(in nsIDRef aTarget);
+};
+
+%{C++
+// category and observer event defines (XXX not yet implemented)
+#define IPC_SERVICE_STARTUP_CATEGORY "ipc-startup-category"
+#define IPC_SERVICE_STARTUP_TOPIC "ipc-startup"
+#define IPC_SERVICE_SHUTDOWN_TOPIC "ipc-shutdown"
+%}
diff --git a/src/libs/xpcom18a4/ipc/ipcd/client/public/ipcdclient.h b/src/libs/xpcom18a4/ipc/ipcd/client/public/ipcdclient.h
new file mode 100644
index 00000000..5cbed879
--- /dev/null
+++ b/src/libs/xpcom18a4/ipc/ipcd/client/public/ipcdclient.h
@@ -0,0 +1,326 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Mozilla IPC.
+ *
+ * The Initial Developer of the Original Code is IBM Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 2004
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Darin Fisher <darin@meer.net>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#ifndef ipcdclient_h__
+#define ipcdclient_h__
+
+/*****************************************************************************
+ * This file provides a client-side API to the IPC daemon.
+ *
+ * This API can be used to communicate with other clients of the IPC daemon
+ * as well as modules running inside the IPC daemon.
+ *
+ * This API is meant to be used only on the application's main thread. It is
+ * assumed that callbacks can be dispatched via the main thread's event queue.
+ */
+
+#include "nscore.h"
+#include "nsID.h"
+#include "nsError.h"
+#include "ipcIMessageObserver.h"
+#include "ipcIClientObserver.h"
+
+/* This API is only provided for the extensions compiled into the IPCDC
+ * library, hence this API is hidden in the final DSO. */
+#define IPC_METHOD NS_HIDDEN_(nsresult)
+
+/* This value can be used to represent the client id of any client connected
+ * to the IPC daemon. */
+#define IPC_SENDER_ANY PR_UINT32_MAX
+
+/* This error code can only be returned by OnMessageAvailable, when called by
+ * IPC_WaitMessage. See IPC_WaitMessage for a description of how this error
+ * code may be used. */
+#define IPC_WAIT_NEXT_MESSAGE \
+ NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_GENERAL, 10)
+
+#ifdef VBOX
+/* This error code can only be returned by the selector functions called by
+ * WaitTarget. The message should be dropped without processing. */
+#define IPC_DISCARD_MESSAGE \
+ NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_GENERAL, 12)
+#endif /* VBOX */
+
+/* This error code is returned by IPC_WaitMessage under certain conditions. */
+#define IPC_ERROR_WOULD_BLOCK NS_BASE_STREAM_WOULD_BLOCK
+
+/*****************************************************************************
+ * Initialization and Shutdown
+ */
+
+// XXX limit these to the main thread, and call them from our module's ctor/dtor?
+
+/**
+ * Connects this process to the IPC daemon and initializes it for use as a
+ * client of the IPC daemon. This function must be called once before any
+ * other methods defined in this file can be used.
+ *
+ * @returns NS_ERROR_ALREADY_INITIALIZED if IPC_Shutdown was not called since
+ * the last time IPC_Init was called.
+ */
+IPC_METHOD IPC_Init();
+
+/**
+ * Disconnects this process from the IPC daemon. After this function is
+ * called, no other methods in this file except for IPC_Init may be called.
+ *
+ * This function calls all client observers (registered with
+ * IPC_AddClientObserver) before it actually disconnects from the IPC daemon,
+ * with aClientID parameter of the OnClientStateChange callback specially
+ * set to IPC_SENDER_ANY and aClientState set to CLIENT_DOWN. This gives
+ * the interested party an opportunity to perform a proper shutdown procedure.
+ * All IPC methods are still available inside OnClientStateChange call except
+ * the IPC_WaitMessage function, that will immediately fail with an error.
+ * OnClientStateChange is called on the same thread where this function is
+ * called.
+ *
+ * @returns NS_ERROR_NOT_INITIALIZED if IPC_Init has not been called or if
+ * IPC_Init did not return a success code.
+ */
+IPC_METHOD IPC_Shutdown();
+
+
+/*****************************************************************************
+ * The core messaging API
+ */
+
+/**
+ * Call this method to define a message target. A message target is defined
+ * by a UUID and a message observer. This observer is notified asynchronously
+ * whenever a message is sent to this target in this process.
+ *
+ * This function has three main effects:
+ * o If the message target is already defined, then this function simply
+ * resets its message observer.
+ * o If the message target is not already defined, then the message target
+ * is defined and the IPC daemon is notified of the existance of this
+ * message target.
+ * o If null is passed for the message observer, then the message target is
+ * removed, and the daemon is notified of the removal of this message target.
+ *
+ * If aOnCurrentThread is true, then notifications to the observer will occur
+ * on the current thread. This means that there must be a nsIEventTarget
+ * associated with the calling thread. If aOnCurrentThread is false, then
+ * notifications to the observer will occur on a background thread. In which
+ * case, the observer must be threadsafe.
+ */
+IPC_METHOD IPC_DefineTarget(
+ const nsID &aTarget,
+ ipcIMessageObserver *aObserver,
+ PRBool aOnCurrentThread = PR_TRUE
+);
+
+/**
+ * Call this method to temporarily disable the message observer configured
+ * for a message target.
+ */
+IPC_METHOD IPC_DisableMessageObserver(
+ const nsID &aTarget
+);
+
+/**
+ * Call this method to re-enable the message observer configured for a
+ * message target.
+ */
+IPC_METHOD IPC_EnableMessageObserver(
+ const nsID &aTarget
+);
+
+/**
+ * This function sends a message to the IPC daemon asynchronously. If
+ * aReceiverID is non-zero, then the message is forwarded to the client
+ * corresponding to that identifier.
+ *
+ * If there is no client corresponding to aRecieverID, then the IPC daemon will
+ * simply drop the message.
+ */
+IPC_METHOD IPC_SendMessage(
+ PRUint32 aReceiverID,
+ const nsID &aTarget,
+ const PRUint8 *aData,
+ PRUint32 aDataLen
+);
+
+/**
+ * This function blocks the calling thread until a message for the given target
+ * is received (optionally from the specified client).
+ *
+ * The aSenderID parameter is interpreted as follows:
+ * o If aSenderID is 0, then this function waits for a message to be sent by
+ * the IPC daemon.
+ * o If aSenderID is IPC_SENDER_ANY, then this function waits for a message
+ * to be sent from any source.
+ * o Otherwise, this function waits for a message to be sent by the client
+ * with ID given by aSenderID. If aSenderID does not identify a valid
+ * client, or if the client dies while waiting, then this function will
+ * return an error.
+ *
+ * The aObserver parameter is interpreted as follows:
+ * o If aObserver is null, then the default message observer for the target
+ * is invoked when the next message is received.
+ * o Otherwise, aObserver will be inovked when the next message is received.
+ *
+ * The aConsumer parameter is interpreted as follows:
+ * o If aObserver is null or aConsumer is null, this parameter is ignored.
+ * o Otherwise, aConsumer will be invoked right after (and only if) aObserver
+ * has accepted some message. It will receive exactly the same message
+ * that was accepted by aObserver.
+ *
+ * The aTimeout parameter is interpreted as follows:
+ * o If aTimeout is PR_INTERVAL_NO_TIMEOUT, then this function will block
+ * until a matching message is received.
+ * o If aTimeout is PR_INTERVAL_NO_WAIT, then this function will only inspect
+ * the current queue of messages. If no matching message is found, then
+ * IPC_ERROR_WOULD_BLOCK is returned.
+ * o Otherwise, aTimeout specifies the maximum amount of time to wait for a
+ * matching message to be received. If no matching message is found after
+ * the timeout expires, then IPC_ERROR_WOULD_BLOCK is returned.
+ *
+ * If aObserver's OnMessageAvailable function returns IPC_WAIT_NEXT_MESSAGE,
+ * then the function will continue blocking until the next matching message
+ * is received. Bypassed messages will be dispatched to the default message
+ * observer when the event queue, associated with the thread that called
+ * IPC_DefineTarget, is processed.
+ *
+ * There is a special case if aSenderID is IPC_SENDER_ANY and one of IPC clients
+ * dies while this function waits for a message. In this case, aObserver's
+ * OnMessageAvailable function is called with a special set of arguments:
+ * dead client id, empty target id and null message (both data and data length
+ * are zeroes). If it returns IPC_WAIT_NEXT_MESSAGE, IPC_WaitMessage continues
+ * blocking as usual, otherwise an error is immediately returned to the caller.
+ *
+ * aObserver is not expected to use any IPC system functons from within its
+ * OnMessageAvailable implementation. This is because the IPC system is not
+ * re-entrant during invocation of OnMessageAvailable function, and making other
+ * IPC calls can lead to unexpected results (like message losses or traps). On
+ * the contrary, aConsumer's OnMessageAvailable function is allowed to use the
+ * IPC system without any limitations.
+ *
+ * This function runs the risk of hanging the calling thread indefinitely if
+ * no matching message is ever received.
+ */
+IPC_METHOD IPC_WaitMessage(
+ PRUint32 aSenderID,
+ const nsID &aTarget,
+ ipcIMessageObserver *aObserver = nsnull,
+ ipcIMessageObserver *aConsumer = nsnull,
+ PRIntervalTime aTimeout = PR_INTERVAL_NO_TIMEOUT
+);
+
+/*****************************************************************************/
+
+/**
+ * Returns the "ClientID" of the current process.
+ */
+IPC_METHOD IPC_GetID(
+ PRUint32 *aClientID
+);
+
+/**
+ * Adds a new name for the current process. The IPC daemon is notified of this
+ * change, which allows other processes to discover this process by the given
+ * name.
+ */
+IPC_METHOD IPC_AddName(
+ const char *aName
+);
+
+/**
+ * Removes a name associated with the current process.
+ */
+IPC_METHOD IPC_RemoveName(
+ const char *aName
+);
+
+/**
+ * Adds client observer. Will be called on the main thread.
+ */
+IPC_METHOD IPC_AddClientObserver(
+ ipcIClientObserver *aObserver
+);
+
+/**
+ * Removes client observer.
+ */
+IPC_METHOD IPC_RemoveClientObserver(
+ ipcIClientObserver *aObserver
+);
+
+/**
+ * Resolves the given client name to a client ID of a process connected to
+ * the IPC daemon.
+ */
+IPC_METHOD IPC_ResolveClientName(
+ const char *aName,
+ PRUint32 *aClientID
+);
+
+/**
+ * Tests whether the client is connected to the IPC daemon.
+ */
+IPC_METHOD IPC_ClientExists(
+ PRUint32 aClientID,
+ PRBool *aResult
+);
+
+/*****************************************************************************/
+
+/**
+ * This class can be used to temporarily disable the default message observer
+ * defined for a particular message target.
+ */
+class ipcDisableMessageObserverForScope
+{
+public:
+ ipcDisableMessageObserverForScope(const nsID &aTarget)
+ : mTarget(aTarget)
+ {
+ IPC_DisableMessageObserver(mTarget);
+ }
+
+ ~ipcDisableMessageObserverForScope()
+ {
+ IPC_EnableMessageObserver(mTarget);
+ }
+
+private:
+ const nsID &mTarget;
+};
+
+#define IPC_DISABLE_MESSAGE_OBSERVER_FOR_SCOPE(_t) \
+ ipcDisableMessageObserverForScope ipc_dmo_for_scope##_t(_t)
+
+#endif /* ipcdclient_h__ */
diff --git a/src/libs/xpcom18a4/ipc/ipcd/client/src/.cvsignore b/src/libs/xpcom18a4/ipc/ipcd/client/src/.cvsignore
new file mode 100644
index 00000000..f3c7a7c5
--- /dev/null
+++ b/src/libs/xpcom18a4/ipc/ipcd/client/src/.cvsignore
@@ -0,0 +1 @@
+Makefile
diff --git a/src/libs/xpcom18a4/ipc/ipcd/client/src/Makefile.in b/src/libs/xpcom18a4/ipc/ipcd/client/src/Makefile.in
new file mode 100644
index 00000000..b2854e35
--- /dev/null
+++ b/src/libs/xpcom18a4/ipc/ipcd/client/src/Makefile.in
@@ -0,0 +1,101 @@
+# vim: noexpandtab ts=4 sw=4
+# ***** BEGIN LICENSE BLOCK *****
+# Version: MPL 1.1/GPL 2.0/LGPL 2.1
+#
+# The contents of this file are subject to the Mozilla Public License Version
+# 1.1 (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+# http://www.mozilla.org/MPL/
+#
+# Software distributed under the License is distributed on an "AS IS" basis,
+# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+# for the specific language governing rights and limitations under the
+# License.
+#
+# The Original Code is Mozilla IPC.
+#
+# The Initial Developer of the Original Code is
+# Netscape Communications Corporation.
+# Portions created by the Initial Developer are Copyright (C) 2002
+# the Initial Developer. All Rights Reserved.
+#
+# Contributor(s):
+# Darin Fisher <darin@netscape.com>
+#
+# Alternatively, the contents of this file may be used under the terms of
+# either the GNU General Public License Version 2 or later (the "GPL"), or
+# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+# in which case the provisions of the GPL or the LGPL are applicable instead
+# of those above. If you wish to allow use of your version of this file only
+# under the terms of either the GPL or the LGPL, and not to allow others to
+# use your version of this file under the terms of the MPL, indicate your
+# decision by deleting the provisions above and replace them with the notice
+# and other provisions required by the GPL or the LGPL. If you do not delete
+# the provisions above, a recipient may use your version of this file under
+# the terms of any one of the MPL, the GPL or the LGPL.
+#
+# ***** END LICENSE BLOCK *****
+
+DEPTH = ../../../..
+topsrcdir = @top_srcdir@
+srcdir = @srcdir@
+VPATH = @srcdir@
+
+include $(DEPTH)/config/autoconf.mk
+
+MODULE = ipcd
+LIBRARY_NAME = ipcdc
+EXPORT_LIBRARY = 1
+IS_COMPONENT = 1
+MODULE_NAME = ipcdclient
+
+REQUIRES = \
+ xpcom \
+ string \
+ $(NULL)
+
+CPPSRCS = \
+ ipcdclient.cpp \
+ ipcService.cpp \
+ ipcModuleFactory.cpp \
+ $(NULL)
+
+ifeq ($(OS_ARCH),WINNT)
+CPPSRCS += ipcConnectionWin.cpp
+else
+ifeq ($(OS_ARCH),BeOS)
+CPPSRCS += ipcConnectionStub.cpp
+else
+CPPSRCS += ipcConnectionUnix.cpp
+endif
+endif
+
+LOCAL_INCLUDES = \
+ -I$(srcdir)/../../shared/src \
+ -I$(srcdir)/../../extensions/lock/src \
+ -I$(srcdir)/../../extensions/transmngr/src \
+ -I$(srcdir)/../../extensions/transmngr/common \
+ $(NULL)
+
+SHARED_LIBRARY_LIBS = \
+ $(DIST)/lib/$(LIB_PREFIX)ipcdshared_s.$(LIB_SUFFIX) \
+ $(DIST)/lib/$(LIB_PREFIX)ipcdlock_s.$(LIB_SUFFIX) \
+ $(DIST)/lib/$(LIB_PREFIX)transmgr_s.$(LIB_SUFFIX) \
+ $(DIST)/lib/$(LIB_PREFIX)transmgrcom_s.$(LIB_SUFFIX) \
+ $(NULL)
+
+ifneq ($(BUILD_DCONNECT),)
+DEFINES += -DBUILD_DCONNECT
+LOCAL_INCLUDES += -I$(srcdir)/../../extensions/dconnect/src
+SHARED_LIBRARY_LIBS += $(DIST)/lib/$(LIB_PREFIX)ipcddconnect_s.$(LIB_SUFFIX) \
+ $(DIST)/lib/$(LIB_PREFIX)ipcdutil_s.$(LIB_SUFFIX) \
+ $(NULL)
+endif
+
+EXTRA_DSO_LDOPTS = \
+ $(LIBS_DIR) \
+ $(EXTRA_DSO_LIBS) \
+ $(MOZ_COMPONENT_LIBS) \
+ $(NULL)
+
+include $(topsrcdir)/config/rules.mk
diff --git a/src/libs/xpcom18a4/ipc/ipcd/client/src/Makefile.kup b/src/libs/xpcom18a4/ipc/ipcd/client/src/Makefile.kup
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/src/libs/xpcom18a4/ipc/ipcd/client/src/Makefile.kup
diff --git a/src/libs/xpcom18a4/ipc/ipcd/client/src/ipcConnection.h b/src/libs/xpcom18a4/ipc/ipcd/client/src/ipcConnection.h
new file mode 100644
index 00000000..8773daa5
--- /dev/null
+++ b/src/libs/xpcom18a4/ipc/ipcd/client/src/ipcConnection.h
@@ -0,0 +1,147 @@
+/* vim:set ts=2 sw=2 et cindent: */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Mozilla IPC.
+ *
+ * The Initial Developer of the Original Code is IBM Corporation.
+ * Portions created by IBM Corporation are Copyright (C) 2003
+ * IBM Corporation. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Darin Fisher <darin@meer.net>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#ifndef ipcConnection_h__
+#define ipcConnection_h__
+
+#include "nscore.h"
+
+class ipcMessage;
+
+#define IPC_METHOD_PRIVATE_(type) NS_HIDDEN_(type)
+#define IPC_METHOD_PRIVATE IPC_METHOD_PRIVATE_(nsresult)
+
+/* ------------------------------------------------------------------------- */
+/* Platform specific IPC connection API.
+ */
+
+typedef void (* ipcCallbackFunc)(void *);
+
+/**
+ * IPC_Connect
+ *
+ * This function causes a connection to the IPC daemon to be established.
+ * If a connection already exists, then this function will be ignored.
+ *
+ * @param daemonPath
+ * Specifies the path to the IPC daemon executable.
+ *
+ * NOTE: This function must be called on the main thread.
+ */
+IPC_METHOD_PRIVATE IPC_Connect(const char *daemonPath);
+
+/**
+ * IPC_Disconnect
+ *
+ * This function causes a connection to the IPC daemon to be closed. Any
+ * unsent messages (IPC_SendMsg puts messages on a queue) will be sent to the
+ * IPC daemon before the connection is closed.
+ *
+ * NOTE: This function must be called on the main thread.
+ */
+IPC_METHOD_PRIVATE IPC_Disconnect();
+
+/**
+ * IPC_SendMsg
+ *
+ * This function sends a message to the IPC daemon. Typically, the message
+ * is put on a queue, to be delivered asynchronously to the IPC daemon. The
+ * ipcMessage object will be deleted when IPC_SendMsg is done with it. The
+ * caller must not touch |msg| after passing it to IPC_SendMsg.
+ *
+ * IPC_SendMsg cannot be called before IPC_Connect or after IPC_Disconnect.
+ *
+ * NOTE: This function may be called on any thread.
+ */
+IPC_METHOD_PRIVATE IPC_SendMsg(ipcMessage *msg);
+
+/**
+ * IPC_DoCallback
+ *
+ * This function executes a callback function on the same background thread
+ * that calls IPC_OnConnectionEnd and IPC_OnMessageAvailable.
+ *
+ * If this function succeeds, then the caller is guaranteed that |func| will
+ * be called. This guarantee is important because it allows the caller to
+ * free any memory associated with |arg| once |func| has been called.
+ *
+ * NOTE: This function may be called on any thread.
+ */
+IPC_METHOD_PRIVATE IPC_DoCallback(ipcCallbackFunc func, void *arg);
+
+/* ------------------------------------------------------------------------- */
+/* Cross-platform IPC connection methods.
+ */
+
+/**
+ * IPC_SpawnDaemon
+ *
+ * This function launches the IPC daemon process. It is called by the platform
+ * specific IPC_Connect implementation. It should not return until the daemon
+ * process is ready to receive a client connection or an error occurs.
+ *
+ * @param daemonPath
+ * Specifies the path to the IPC daemon executable.
+ */
+IPC_METHOD_PRIVATE IPC_SpawnDaemon(const char *daemonPath);
+
+/* ------------------------------------------------------------------------- */
+/* IPC connection callbacks (not implemented by the connection code).
+ *
+ * NOTE: These functions execute on a background thread!!
+ */
+
+/**
+ * IPC_OnConnectionEnd
+ *
+ * This function is called whenever the connection to the IPC daemon has been
+ * terminated. If terminated due to an abnormal error, then the error will be
+ * described by the |error| parameter. If |error| is NS_OK, then it means the
+ * connection was closed in response to a call to IPC_Disconnect.
+ */
+IPC_METHOD_PRIVATE_(void) IPC_OnConnectionEnd(nsresult error);
+
+/**
+ * IPC_OnMessageAvailable
+ *
+ * This function is called whenever an incoming message is read from the IPC
+ * daemon. The ipcMessage object, |msg|, must be deleted by the implementation
+ * of IPC_OnMessageAvailable when the object is no longer needed.
+ */
+IPC_METHOD_PRIVATE_(void) IPC_OnMessageAvailable(ipcMessage *msg);
+
+#endif // ipcConnection_h__
diff --git a/src/libs/xpcom18a4/ipc/ipcd/client/src/ipcConnectionStub.cpp b/src/libs/xpcom18a4/ipc/ipcd/client/src/ipcConnectionStub.cpp
new file mode 100644
index 00000000..8b985785
--- /dev/null
+++ b/src/libs/xpcom18a4/ipc/ipcd/client/src/ipcConnectionStub.cpp
@@ -0,0 +1,70 @@
+/* vim:set ts=2 sw=2 et cindent: */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Mozilla IPC.
+ *
+ * The Initial Developer of the Original Code is IBM Corporation.
+ * Portions created by IBM Corporation are Copyright (C) 2003
+ * IBM Corporation. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Darin Fisher <darin@meer.net>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#include "ipcConnection.h"
+#include "nsError.h"
+
+//-----------------------------------------------------------------------------
+// use this file as a template to add client-side IPC connectivity.
+//
+// NOTE: if your platform supports local domain TCP sockets, then you should
+// be able to make use of ipcConnectionUnix.cpp.
+//-----------------------------------------------------------------------------
+
+nsresult
+IPC_Connect(const char *daemonPath)
+{
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+nsresult
+IPC_Disconnect()
+{
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+nsresult
+IPC_SendMsg(ipcMessage *msg)
+{
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+nsresult
+IPC_DoCallback(ipcCallbackFunc func, void *arg)
+{
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
diff --git a/src/libs/xpcom18a4/ipc/ipcd/client/src/ipcConnectionUnix.cpp b/src/libs/xpcom18a4/ipc/ipcd/client/src/ipcConnectionUnix.cpp
new file mode 100644
index 00000000..5cd7a0c8
--- /dev/null
+++ b/src/libs/xpcom18a4/ipc/ipcd/client/src/ipcConnectionUnix.cpp
@@ -0,0 +1,615 @@
+/* vim:set ts=2 sw=2 et cindent: */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Mozilla IPC.
+ *
+ * The Initial Developer of the Original Code is IBM Corporation.
+ * Portions created by IBM Corporation are Copyright (C) 2003
+ * IBM Corporation. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Darin Fisher <darin@meer.net>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#include "private/pprio.h"
+#include "prerror.h"
+#include "prthread.h"
+#include "prlock.h"
+#include "prlog.h" // for PR_ASSERT (we don't actually use NSPR logging)
+#include "prio.h"
+
+#include "ipcConnection.h"
+#include "ipcMessageQ.h"
+#include "ipcConfig.h"
+#include "ipcLog.h"
+
+#ifdef VBOX
+# include "prenv.h"
+# include <stdio.h>
+# include <VBox/log.h>
+#endif
+
+
+//-----------------------------------------------------------------------------
+// NOTE: this code does not need to link with anything but NSPR. that is by
+// design, so it can be easily reused in other projects that want to
+// talk with mozilla's IPC daemon, but don't want to depend on xpcom.
+// we depend at most on some xpcom header files, but no xpcom runtime
+// symbols are used.
+//-----------------------------------------------------------------------------
+
+
+// single user systems, like OS/2, don't need these security checks.
+#ifndef XP_OS2
+#define IPC_SKIP_SECURITY_CHECKS
+#endif
+
+#ifndef IPC_SKIP_SECURITY_CHECKS
+#include <unistd.h>
+#include <sys/stat.h>
+#endif
+
+static PRStatus
+DoSecurityCheck(PRFileDesc *fd, const char *path)
+{
+#ifndef IPC_SKIP_SECURITY_CHECKS
+ //
+ // now that we have a connected socket; do some security checks on the
+ // file descriptor.
+ //
+ // (1) make sure owner matches
+ // (2) make sure permissions match expected permissions
+ //
+ // if these conditions aren't met then bail.
+ //
+ int unix_fd = PR_FileDesc2NativeHandle(fd);
+
+ struct stat st;
+ if (fstat(unix_fd, &st) == -1) {
+ LOG(("stat failed"));
+ return PR_FAILURE;
+ }
+
+ if (st.st_uid != getuid() && st.st_uid != geteuid()) {
+ //
+ // on OSX 10.1.5, |fstat| has a bug when passed a file descriptor to
+ // a socket. it incorrectly returns a UID of 0. however, |stat|
+ // succeeds, but using |stat| introduces a race condition.
+ //
+ // XXX come up with a better security check.
+ //
+ if (st.st_uid != 0) {
+ LOG(("userid check failed"));
+ return PR_FAILURE;
+ }
+ if (stat(path, &st) == -1) {
+ LOG(("stat failed"));
+ return PR_FAILURE;
+ }
+ if (st.st_uid != getuid() && st.st_uid != geteuid()) {
+ LOG(("userid check failed"));
+ return PR_FAILURE;
+ }
+ }
+#endif
+ return PR_SUCCESS;
+}
+
+//-----------------------------------------------------------------------------
+
+struct ipcCallback : public ipcListNode<ipcCallback>
+{
+ ipcCallbackFunc func;
+ void *arg;
+};
+
+typedef ipcList<ipcCallback> ipcCallbackQ;
+
+//-----------------------------------------------------------------------------
+
+struct ipcConnectionState
+{
+ PRLock *lock;
+ PRPollDesc fds[2];
+ ipcCallbackQ callback_queue;
+ ipcMessageQ send_queue;
+ PRUint32 send_offset; // amount of send_queue.First() already written.
+ ipcMessage *in_msg;
+ PRBool shutdown;
+};
+
+#define SOCK 0
+#define POLL 1
+
+static void
+ConnDestroy(ipcConnectionState *s)
+{
+ if (s->lock)
+ PR_DestroyLock(s->lock);
+
+ if (s->fds[SOCK].fd)
+ PR_Close(s->fds[SOCK].fd);
+
+ if (s->fds[POLL].fd)
+ PR_DestroyPollableEvent(s->fds[POLL].fd);
+
+ if (s->in_msg)
+ delete s->in_msg;
+
+ s->send_queue.DeleteAll();
+ delete s;
+}
+
+static ipcConnectionState *
+ConnCreate(PRFileDesc *fd)
+{
+ ipcConnectionState *s = new ipcConnectionState;
+ if (!s)
+ return NULL;
+
+ s->lock = PR_NewLock();
+ s->fds[SOCK].fd = NULL;
+ s->fds[POLL].fd = PR_NewPollableEvent();
+ s->send_offset = 0;
+ s->in_msg = NULL;
+ s->shutdown = PR_FALSE;
+
+ if (!s->lock || !s->fds[1].fd)
+ {
+ ConnDestroy(s);
+ return NULL;
+ }
+
+ // disable inheritance of the IPC socket by children started
+ // using non-NSPR process API
+ PRStatus status = PR_SetFDInheritable(fd, PR_FALSE);
+ if (status != PR_SUCCESS)
+ {
+ LOG(("coudn't make IPC socket non-inheritable [err=%d]\n", PR_GetError()));
+ return NULL;
+ }
+
+ // store this only if we are going to succeed.
+ s->fds[SOCK].fd = fd;
+
+ return s;
+}
+
+static nsresult
+ConnRead(ipcConnectionState *s)
+{
+ char buf[1024];
+ nsresult rv = NS_OK;
+ PRInt32 n;
+
+ do
+ {
+ n = PR_Read(s->fds[SOCK].fd, buf, sizeof(buf));
+ if (n < 0)
+ {
+ PRErrorCode err = PR_GetError();
+ if (err == PR_WOULD_BLOCK_ERROR)
+ {
+ // socket is empty... we need to go back to polling.
+ break;
+ }
+ LOG(("PR_Read returned failure [err=%d]\n", err));
+ rv = NS_ERROR_UNEXPECTED;
+ }
+ else if (n == 0)
+ {
+ LOG(("PR_Read returned EOF\n"));
+ rv = NS_ERROR_UNEXPECTED;
+ }
+ else
+ {
+ const char *pdata = buf;
+ while (n)
+ {
+ PRUint32 bytesRead;
+ PRBool complete;
+
+ if (!s->in_msg)
+ {
+ s->in_msg = new ipcMessage;
+ if (!s->in_msg)
+ {
+ rv = NS_ERROR_OUT_OF_MEMORY;
+ break;
+ }
+ }
+
+ if (s->in_msg->ReadFrom(pdata, n, &bytesRead, &complete) != PR_SUCCESS)
+ {
+ LOG(("error reading IPC message\n"));
+ rv = NS_ERROR_UNEXPECTED;
+ break;
+ }
+
+ PR_ASSERT(PRUint32(n) >= bytesRead);
+ n -= bytesRead;
+ pdata += bytesRead;
+
+ if (complete)
+ {
+ // protect against weird re-entrancy cases...
+ ipcMessage *m = s->in_msg;
+ s->in_msg = NULL;
+
+ IPC_OnMessageAvailable(m);
+ }
+ }
+ }
+ }
+ while (NS_SUCCEEDED(rv));
+
+ return rv;
+}
+
+static nsresult
+ConnWrite(ipcConnectionState *s)
+{
+ nsresult rv = NS_OK;
+
+ PR_Lock(s->lock);
+
+ // write one message and then return.
+ if (s->send_queue.First())
+ {
+ PRInt32 n = PR_Write(s->fds[SOCK].fd,
+ s->send_queue.First()->MsgBuf() + s->send_offset,
+ s->send_queue.First()->MsgLen() - s->send_offset);
+ if (n <= 0)
+ {
+ PRErrorCode err = PR_GetError();
+ if (err == PR_WOULD_BLOCK_ERROR)
+ {
+ // socket is full... we need to go back to polling.
+ }
+ else
+ {
+ LOG(("error writing to socket [err=%d]\n", err));
+ rv = NS_ERROR_UNEXPECTED;
+ }
+ }
+ else
+ {
+ s->send_offset += n;
+ if (s->send_offset == s->send_queue.First()->MsgLen())
+ {
+ s->send_queue.DeleteFirst();
+ s->send_offset = 0;
+
+ // if the send queue is empty, then we need to stop trying to write.
+ if (s->send_queue.IsEmpty())
+ s->fds[SOCK].in_flags &= ~PR_POLL_WRITE;
+ }
+ }
+ }
+
+ PR_Unlock(s->lock);
+ return rv;
+}
+
+PR_STATIC_CALLBACK(void)
+ConnThread(void *arg)
+{
+ PRInt32 num;
+ nsresult rv = NS_OK;
+
+ ipcConnectionState *s = (ipcConnectionState *) arg;
+
+ // we monitor two file descriptors in this thread. the first (at index 0) is
+ // the socket connection with the IPC daemon. the second (at index 1) is the
+ // pollable event we monitor in order to know when to send messages to the
+ // IPC daemon.
+
+ s->fds[SOCK].in_flags = PR_POLL_READ;
+ s->fds[POLL].in_flags = PR_POLL_READ;
+
+ while (NS_SUCCEEDED(rv))
+ {
+ s->fds[SOCK].out_flags = 0;
+ s->fds[POLL].out_flags = 0;
+
+ //
+ // poll on the IPC socket and NSPR pollable event
+ //
+ num = PR_Poll(s->fds, 2, PR_INTERVAL_NO_TIMEOUT);
+ if (num > 0)
+ {
+ ipcCallbackQ cbs_to_run;
+
+ // check if something has been added to the send queue. if so, then
+ // acknowledge pollable event (wait should not block), and configure
+ // poll flags to find out when we can write.
+
+ if (s->fds[POLL].out_flags & PR_POLL_READ)
+ {
+ PR_WaitForPollableEvent(s->fds[POLL].fd);
+ PR_Lock(s->lock);
+
+ if (!s->send_queue.IsEmpty())
+ s->fds[SOCK].in_flags |= PR_POLL_WRITE;
+
+ if (!s->callback_queue.IsEmpty())
+ s->callback_queue.MoveTo(cbs_to_run);
+
+ PR_Unlock(s->lock);
+ }
+
+ // check if we can read...
+ if (s->fds[SOCK].out_flags & PR_POLL_READ)
+ rv = ConnRead(s);
+
+ // check if we can write...
+ if (s->fds[SOCK].out_flags & PR_POLL_WRITE)
+ rv = ConnWrite(s);
+
+ // check if we have callbacks to run
+ while (!cbs_to_run.IsEmpty())
+ {
+ ipcCallback *cb = cbs_to_run.First();
+ (cb->func)(cb->arg);
+ cbs_to_run.DeleteFirst();
+ }
+
+ // check if we should exit this thread. delay processing a shutdown
+ // request until after all queued up messages have been sent and until
+ // after all queued up callbacks have been run.
+ PR_Lock(s->lock);
+ if (s->shutdown && s->send_queue.IsEmpty() && s->callback_queue.IsEmpty())
+ rv = NS_ERROR_ABORT;
+ PR_Unlock(s->lock);
+ }
+ else
+ {
+ LOG(("PR_Poll returned error %d (%s), os error %d\n", PR_GetError(),
+ PR_ErrorToName(PR_GetError()), PR_GetOSError()));
+ rv = NS_ERROR_UNEXPECTED;
+ }
+ }
+
+ // notify termination of the IPC connection
+ if (rv == NS_ERROR_ABORT)
+ rv = NS_OK;
+ IPC_OnConnectionEnd(rv);
+
+ LOG(("IPC thread exiting\n"));
+}
+
+//-----------------------------------------------------------------------------
+// IPC connection API
+//-----------------------------------------------------------------------------
+
+static ipcConnectionState *gConnState = NULL;
+static PRThread *gConnThread = NULL;
+
+#ifdef DEBUG
+static PRThread *gMainThread = NULL;
+#endif
+
+nsresult
+TryConnect(PRFileDesc **result)
+{
+ PRFileDesc *fd;
+ PRNetAddr addr;
+ PRSocketOptionData opt;
+ // don't use NS_ERROR_FAILURE as we want to detect these kind of errors
+ // in the frontend
+ nsresult rv = NS_ERROR_SOCKET_FAIL;
+
+ fd = PR_OpenTCPSocket(PR_AF_LOCAL);
+ if (!fd)
+ goto end;
+
+ addr.local.family = PR_AF_LOCAL;
+ IPC_GetDefaultSocketPath(addr.local.path, sizeof(addr.local.path));
+
+ // blocking connect... will fail if no one is listening.
+ if (PR_Connect(fd, &addr, PR_INTERVAL_NO_TIMEOUT) == PR_FAILURE)
+ goto end;
+
+#ifdef VBOX
+ if (PR_GetEnv("TESTBOX_UUID"))
+ fprintf(stderr, "IPC socket path: %s\n", addr.local.path);
+ LogRel(("IPC socket path: %s\n", addr.local.path));
+#endif
+
+ // make socket non-blocking
+ opt.option = PR_SockOpt_Nonblocking;
+ opt.value.non_blocking = PR_TRUE;
+ PR_SetSocketOption(fd, &opt);
+
+ // do some security checks on connection socket...
+ if (DoSecurityCheck(fd, addr.local.path) != PR_SUCCESS)
+ goto end;
+
+ *result = fd;
+ return NS_OK;
+
+end:
+ if (fd)
+ PR_Close(fd);
+
+ return rv;
+}
+
+nsresult
+IPC_Connect(const char *daemonPath)
+{
+ // synchronous connect, spawn daemon if necessary.
+
+ PRFileDesc *fd = NULL;
+ nsresult rv = NS_ERROR_FAILURE;
+
+ if (gConnState)
+ return NS_ERROR_ALREADY_INITIALIZED;
+
+ //
+ // here's the connection algorithm: try to connect to an existing daemon.
+ // if the connection fails, then spawn the daemon (wait for it to be ready),
+ // and then retry the connection. it is critical that the socket used to
+ // connect to the daemon not be inherited (this causes problems on RH9 at
+ // least).
+ //
+
+ rv = TryConnect(&fd);
+ if (NS_FAILED(rv))
+ {
+ nsresult rv1 = IPC_SpawnDaemon(daemonPath);
+ if (NS_SUCCEEDED(rv1) || rv != NS_ERROR_SOCKET_FAIL)
+ rv = rv1;
+ if (NS_SUCCEEDED(rv))
+ rv = TryConnect(&fd);
+ }
+
+ if (NS_FAILED(rv))
+ goto end;
+
+ //
+ // ok, we have a connection to the daemon!
+ //
+
+ // build connection state object
+ gConnState = ConnCreate(fd);
+ if (!gConnState)
+ {
+ rv = NS_ERROR_OUT_OF_MEMORY;
+ goto end;
+ }
+ fd = NULL; // connection state now owns the socket
+
+ gConnThread = PR_CreateThread(PR_USER_THREAD,
+ ConnThread,
+ gConnState,
+ PR_PRIORITY_NORMAL,
+ PR_GLOBAL_THREAD,
+ PR_JOINABLE_THREAD,
+ 0);
+ if (!gConnThread)
+ {
+ rv = NS_ERROR_OUT_OF_MEMORY;
+ goto end;
+ }
+
+#ifdef DEBUG
+ gMainThread = PR_GetCurrentThread();
+#endif
+ return NS_OK;
+
+end:
+ if (gConnState)
+ {
+ ConnDestroy(gConnState);
+ gConnState = NULL;
+ }
+ if (fd)
+ PR_Close(fd);
+ return rv;
+}
+
+nsresult
+IPC_Disconnect()
+{
+ // Must disconnect on same thread used to connect!
+ PR_ASSERT(gMainThread == PR_GetCurrentThread());
+
+ if (!gConnState || !gConnThread)
+ return NS_ERROR_NOT_INITIALIZED;
+
+ PR_Lock(gConnState->lock);
+ gConnState->shutdown = PR_TRUE;
+ PR_SetPollableEvent(gConnState->fds[POLL].fd);
+ PR_Unlock(gConnState->lock);
+
+ PR_JoinThread(gConnThread);
+
+ ConnDestroy(gConnState);
+
+ gConnState = NULL;
+ gConnThread = NULL;
+ return NS_OK;
+}
+
+nsresult
+IPC_SendMsg(ipcMessage *msg)
+{
+ if (!gConnState || !gConnThread)
+ return NS_ERROR_NOT_INITIALIZED;
+
+ PR_Lock(gConnState->lock);
+ gConnState->send_queue.Append(msg);
+ PR_SetPollableEvent(gConnState->fds[POLL].fd);
+ PR_Unlock(gConnState->lock);
+
+ return NS_OK;
+}
+
+nsresult
+IPC_DoCallback(ipcCallbackFunc func, void *arg)
+{
+ if (!gConnState || !gConnThread)
+ return NS_ERROR_NOT_INITIALIZED;
+
+ ipcCallback *callback = new ipcCallback;
+ if (!callback)
+ return NS_ERROR_OUT_OF_MEMORY;
+ callback->func = func;
+ callback->arg = arg;
+
+ PR_Lock(gConnState->lock);
+ gConnState->callback_queue.Append(callback);
+ PR_SetPollableEvent(gConnState->fds[POLL].fd);
+ PR_Unlock(gConnState->lock);
+ return NS_OK;
+}
+
+//-----------------------------------------------------------------------------
+
+#ifdef TEST_STANDALONE
+
+void IPC_OnConnectionFault(nsresult rv)
+{
+ LOG(("IPC_OnConnectionFault [rv=%x]\n", rv));
+}
+
+void IPC_OnMessageAvailable(ipcMessage *msg)
+{
+ LOG(("IPC_OnMessageAvailable\n"));
+ delete msg;
+}
+
+int main()
+{
+ IPC_InitLog(">>>");
+ IPC_Connect("/builds/moz-trunk/seamonkey-debug-build/dist/bin/mozilla-ipcd");
+ IPC_Disconnect();
+ return 0;
+}
+
+#endif
diff --git a/src/libs/xpcom18a4/ipc/ipcd/client/src/ipcConnectionWin.cpp b/src/libs/xpcom18a4/ipc/ipcd/client/src/ipcConnectionWin.cpp
new file mode 100644
index 00000000..9a99c195
--- /dev/null
+++ b/src/libs/xpcom18a4/ipc/ipcd/client/src/ipcConnectionWin.cpp
@@ -0,0 +1,332 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Mozilla IPC.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 2002
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Darin Fisher <darin@meer.net>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#include <windows.h>
+
+#include "prprf.h"
+#include "prmon.h"
+#include "prthread.h"
+#include "plevent.h"
+
+#include "nsIServiceManager.h"
+#include "nsIEventQueue.h"
+#include "nsIEventQueueService.h"
+#include "nsAutoLock.h"
+
+#include "ipcConfig.h"
+#include "ipcLog.h"
+#include "ipcConnection.h"
+#include "ipcm.h"
+
+
+//-----------------------------------------------------------------------------
+// NOTE: this code does not need to link with anything but NSPR. that is by
+// design, so it can be easily reused in other projects that want to
+// talk with mozilla's IPC daemon, but don't want to depend on xpcom.
+// we depend at most on some xpcom header files, but no xpcom runtime
+// symbols are used.
+//-----------------------------------------------------------------------------
+
+
+//-----------------------------------------------------------------------------
+// windows message thread
+//-----------------------------------------------------------------------------
+
+#define IPC_WM_SENDMSG (WM_USER + 0x1)
+#define IPC_WM_CALLBACK (WM_USER + 0x2)
+#define IPC_WM_SHUTDOWN (WM_USER + 0x3)
+
+static nsresult ipcThreadStatus = NS_OK;
+static PRThread *ipcThread = NULL;
+static PRMonitor *ipcMonitor = NULL;
+static HWND ipcDaemonHwnd = NULL;
+static HWND ipcLocalHwnd = NULL;
+static PRBool ipcShutdown = PR_FALSE; // not accessed on message thread!!
+
+//-----------------------------------------------------------------------------
+// window proc
+//-----------------------------------------------------------------------------
+
+static LRESULT CALLBACK
+ipcThreadWindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
+{
+ LOG(("got message [msg=%x wparam=%x lparam=%x]\n", uMsg, wParam, lParam));
+
+ if (uMsg == WM_COPYDATA) {
+ COPYDATASTRUCT *cd = (COPYDATASTRUCT *) lParam;
+ if (cd && cd->lpData) {
+ ipcMessage *msg = new ipcMessage();
+ PRUint32 bytesRead;
+ PRBool complete;
+ PRStatus rv = msg->ReadFrom((const char *) cd->lpData, cd->cbData,
+ &bytesRead, &complete);
+ if (rv == PR_SUCCESS && complete)
+ IPC_OnMessageAvailable(msg); // takes ownership of msg
+ else {
+ LOG((" unable to deliver message [complete=%u]\n", complete));
+ delete msg;
+ }
+ }
+ return TRUE;
+ }
+
+ if (uMsg == IPC_WM_SENDMSG) {
+ ipcMessage *msg = (ipcMessage *) lParam;
+ if (msg) {
+ LOG((" sending message...\n"));
+ COPYDATASTRUCT cd;
+ cd.dwData = GetCurrentProcessId();
+ cd.cbData = (DWORD) msg->MsgLen();
+ cd.lpData = (PVOID) msg->MsgBuf();
+ SendMessageA(ipcDaemonHwnd, WM_COPYDATA, (WPARAM) hWnd, (LPARAM) &cd);
+ LOG((" done.\n"));
+ delete msg;
+ }
+ return 0;
+ }
+
+ if (uMsg == IPC_WM_CALLBACK) {
+ ipcCallbackFunc func = (ipcCallbackFunc) wParam;
+ void *arg = (void *) lParam;
+ (func)(arg);
+ return 0;
+ }
+
+ if (uMsg == IPC_WM_SHUTDOWN) {
+ IPC_OnConnectionEnd(NS_OK);
+ PostQuitMessage(0);
+ return 0;
+ }
+
+ return DefWindowProc(hWnd, uMsg, wParam, lParam);
+}
+
+//-----------------------------------------------------------------------------
+// ipc thread functions
+//-----------------------------------------------------------------------------
+
+static void
+ipcThreadFunc(void *arg)
+{
+ LOG(("entering message thread\n"));
+
+ DWORD pid = GetCurrentProcessId();
+
+ WNDCLASS wc;
+ memset(&wc, 0, sizeof(wc));
+ wc.lpfnWndProc = ipcThreadWindowProc;
+ wc.lpszClassName = IPC_CLIENT_WINDOW_CLASS;
+ RegisterClass(&wc);
+
+ char wName[sizeof(IPC_CLIENT_WINDOW_NAME_PREFIX) + 20];
+ PR_snprintf(wName, sizeof(wName), "%s%u", IPC_CLIENT_WINDOW_NAME_PREFIX, pid);
+
+ ipcLocalHwnd = CreateWindow(IPC_CLIENT_WINDOW_CLASS, wName,
+ 0, 0, 0, 10, 10, NULL, NULL, NULL, NULL);
+
+ {
+ nsAutoMonitor mon(ipcMonitor);
+ if (!ipcLocalHwnd)
+ ipcThreadStatus = NS_ERROR_FAILURE;
+ mon.Notify();
+ }
+
+ if (ipcLocalHwnd) {
+ MSG msg;
+ while (GetMessage(&msg, ipcLocalHwnd, 0, 0))
+ DispatchMessage(&msg);
+
+ ipcShutdown = PR_TRUE; // assuming atomic memory write
+
+ DestroyWindow(ipcLocalHwnd);
+ ipcLocalHwnd = NULL;
+ }
+
+ LOG(("exiting message thread\n"));
+ return;
+}
+
+static PRStatus
+ipcThreadInit()
+{
+ if (ipcThread)
+ return PR_FAILURE;
+
+ ipcShutdown = PR_FALSE;
+
+ ipcMonitor = PR_NewMonitor();
+ if (!ipcMonitor)
+ return PR_FAILURE;
+
+ // spawn message thread
+ ipcThread = PR_CreateThread(PR_USER_THREAD, ipcThreadFunc, NULL,
+ PR_PRIORITY_NORMAL, PR_GLOBAL_THREAD,
+ PR_JOINABLE_THREAD, 0);
+ if (!ipcThread) {
+ NS_WARNING("thread creation failed");
+ PR_DestroyMonitor(ipcMonitor);
+ ipcMonitor = NULL;
+ return PR_FAILURE;
+ }
+
+ // wait for hidden window to be created
+ {
+ nsAutoMonitor mon(ipcMonitor);
+ while (!ipcLocalHwnd && NS_SUCCEEDED(ipcThreadStatus))
+ mon.Wait();
+ }
+
+ if (NS_FAILED(ipcThreadStatus)) {
+ NS_WARNING("message thread failed");
+ return PR_FAILURE;
+ }
+
+ return PR_SUCCESS;
+}
+
+static PRStatus
+ipcThreadShutdown()
+{
+ if (PR_AtomicSet(&ipcShutdown, PR_TRUE) == PR_FALSE) {
+ LOG(("posting IPC_WM_SHUTDOWN message\n"));
+ PostMessage(ipcLocalHwnd, IPC_WM_SHUTDOWN, 0, 0);
+ }
+
+ LOG(("joining w/ message thread...\n"));
+ PR_JoinThread(ipcThread);
+ ipcThread = NULL;
+
+ //
+ // ok, now the message thread is dead
+ //
+
+ PR_DestroyMonitor(ipcMonitor);
+ ipcMonitor = NULL;
+
+ return PR_SUCCESS;
+}
+
+//-----------------------------------------------------------------------------
+// windows specific IPC connection impl
+//-----------------------------------------------------------------------------
+
+nsresult
+IPC_Disconnect()
+{
+ LOG(("IPC_Disconnect\n"));
+
+ //XXX mHaveConnection = PR_FALSE;
+
+ if (!ipcDaemonHwnd)
+ return NS_ERROR_NOT_INITIALIZED;
+
+ if (ipcThread)
+ ipcThreadShutdown();
+
+ // clear our reference to the daemon's HWND.
+ ipcDaemonHwnd = NULL;
+ return NS_OK;
+}
+
+nsresult
+IPC_Connect(const char *daemonPath)
+{
+ LOG(("IPC_Connect\n"));
+
+ NS_ENSURE_TRUE(ipcDaemonHwnd == NULL, NS_ERROR_ALREADY_INITIALIZED);
+ nsresult rv;
+
+ ipcDaemonHwnd = FindWindow(IPC_WINDOW_CLASS, IPC_WINDOW_NAME);
+ if (!ipcDaemonHwnd) {
+ LOG((" daemon does not appear to be running\n"));
+ //
+ // daemon does not exist; spawn daemon...
+ //
+ rv = IPC_SpawnDaemon(daemonPath);
+ if (NS_FAILED(rv))
+ return rv;
+
+ ipcDaemonHwnd = FindWindow(IPC_WINDOW_CLASS, IPC_WINDOW_NAME);
+ if (!ipcDaemonHwnd)
+ return NS_ERROR_FAILURE;
+ }
+
+ //
+ // delay creation of the message thread until we know the daemon exists.
+ //
+ if (!ipcThread && ipcThreadInit() != PR_SUCCESS) {
+ ipcDaemonHwnd = NULL;
+ return NS_ERROR_FAILURE;
+ }
+
+ return NS_OK;
+}
+
+nsresult
+IPC_SendMsg(ipcMessage *msg)
+{
+ LOG(("IPC_SendMsg\n"));
+
+ if (ipcShutdown) {
+ LOG(("unable to send message b/c message thread is shutdown\n"));
+ goto loser;
+ }
+ if (!PostMessage(ipcLocalHwnd, IPC_WM_SENDMSG, 0, (LPARAM) msg)) {
+ LOG((" PostMessage failed w/ error = %u\n", GetLastError()));
+ goto loser;
+ }
+ return NS_OK;
+loser:
+ delete msg;
+ return NS_ERROR_FAILURE;
+}
+
+nsresult
+IPC_DoCallback(ipcCallbackFunc func, void *arg)
+{
+ LOG(("IPC_DoCallback\n"));
+
+ if (ipcShutdown) {
+ LOG(("unable to send message b/c message thread is shutdown\n"));
+ return NS_ERROR_FAILURE;
+ }
+ if (!PostMessage(ipcLocalHwnd, IPC_WM_CALLBACK, (WPARAM) func, (LPARAM) arg)) {
+ LOG((" PostMessage failed w/ error = %u\n", GetLastError()));
+ return NS_ERROR_FAILURE;
+ }
+ return NS_OK;
+}
diff --git a/src/libs/xpcom18a4/ipc/ipcd/client/src/ipcModuleFactory.cpp b/src/libs/xpcom18a4/ipc/ipcd/client/src/ipcModuleFactory.cpp
new file mode 100644
index 00000000..7dcd6eab
--- /dev/null
+++ b/src/libs/xpcom18a4/ipc/ipcd/client/src/ipcModuleFactory.cpp
@@ -0,0 +1,196 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Mozilla IPC.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 2002
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Darin Fisher <darin@netscape.com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#include "nsIServiceManager.h"
+#include "nsIGenericFactory.h"
+#include "nsICategoryManager.h"
+#include "ipcdclient.h"
+#include "ipcService.h"
+#include "ipcConfig.h"
+#include "ipcCID.h"
+
+//-----------------------------------------------------------------------------
+// Define the contructor function for the objects
+//
+// NOTE: This creates an instance of objects by using the default constructor
+//-----------------------------------------------------------------------------
+NS_GENERIC_FACTORY_CONSTRUCTOR(ipcService)
+
+// enable this code to make the IPC service auto-start.
+#if 0
+NS_METHOD
+ipcServiceRegisterProc(nsIComponentManager *aCompMgr,
+ nsIFile *aPath,
+ const char *registryLocation,
+ const char *componentType,
+ const nsModuleComponentInfo *info)
+{
+ //
+ // add ipcService to the XPCOM startup category
+ //
+ nsCOMPtr<nsICategoryManager> catman(do_GetService(NS_CATEGORYMANAGER_CONTRACTID));
+ if (catman) {
+ nsXPIDLCString prevEntry;
+ catman->AddCategoryEntry(NS_XPCOM_STARTUP_OBSERVER_ID, "ipcService",
+ IPC_SERVICE_CONTRACTID, PR_TRUE, PR_TRUE,
+ getter_Copies(prevEntry));
+ }
+ return NS_OK;
+}
+
+NS_METHOD
+ipcServiceUnregisterProc(nsIComponentManager *aCompMgr,
+ nsIFile *aPath,
+ const char *registryLocation,
+ const nsModuleComponentInfo *info)
+{
+ nsCOMPtr<nsICategoryManager> catman(do_GetService(NS_CATEGORYMANAGER_CONTRACTID));
+ if (catman)
+ catman->DeleteCategoryEntry(NS_XPCOM_STARTUP_OBSERVER_ID,
+ IPC_SERVICE_CONTRACTID, PR_TRUE);
+ return NS_OK;
+}
+#endif
+
+//-----------------------------------------------------------------------------
+// extensions
+
+#include "ipcLockService.h"
+#include "ipcLockCID.h"
+NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(ipcLockService, Init)
+
+#include "tmTransactionService.h"
+NS_GENERIC_FACTORY_CONSTRUCTOR(tmTransactionService)
+
+#ifdef BUILD_DCONNECT
+
+#include "ipcDConnectService.h"
+NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(ipcDConnectService, Init)
+
+// enable this code to make the IPC DCONNECT service auto-start.
+NS_METHOD
+ipcDConnectServiceRegisterProc(nsIComponentManager *aCompMgr,
+ nsIFile *aPath,
+ const char *registryLocation,
+ const char *componentType,
+ const nsModuleComponentInfo *info)
+{
+ //
+ // add ipcService to the XPCOM startup category
+ //
+ nsCOMPtr<nsICategoryManager> catman(do_GetService(NS_CATEGORYMANAGER_CONTRACTID));
+ if (catman) {
+ nsXPIDLCString prevEntry;
+ catman->AddCategoryEntry(NS_XPCOM_STARTUP_OBSERVER_ID, "ipcDConnectService",
+ IPC_DCONNECTSERVICE_CONTRACTID, PR_TRUE, PR_TRUE,
+ getter_Copies(prevEntry));
+ }
+ return NS_OK;
+}
+
+NS_METHOD
+ipcDConnectServiceUnregisterProc(nsIComponentManager *aCompMgr,
+ nsIFile *aPath,
+ const char *registryLocation,
+ const nsModuleComponentInfo *info)
+{
+ nsCOMPtr<nsICategoryManager> catman(do_GetService(NS_CATEGORYMANAGER_CONTRACTID));
+ if (catman)
+ catman->DeleteCategoryEntry(NS_XPCOM_STARTUP_OBSERVER_ID,
+ IPC_DCONNECTSERVICE_CONTRACTID, PR_TRUE);
+ return NS_OK;
+}
+
+#endif // BUILD_DCONNECT
+
+//-----------------------------------------------------------------------------
+// Define a table of CIDs implemented by this module along with other
+// information like the function to create an instance, contractid, and
+// class name.
+//-----------------------------------------------------------------------------
+static const nsModuleComponentInfo components[] = {
+ { IPC_SERVICE_CLASSNAME,
+ IPC_SERVICE_CID,
+ IPC_SERVICE_CONTRACTID,
+ ipcServiceConstructor },
+ /*
+ ipcServiceRegisterProc,
+ ipcServiceUnregisterProc },
+ */
+ //
+ // extensions go here:
+ //
+ { IPC_LOCKSERVICE_CLASSNAME,
+ IPC_LOCKSERVICE_CID,
+ IPC_LOCKSERVICE_CONTRACTID,
+ ipcLockServiceConstructor },
+ { IPC_TRANSACTIONSERVICE_CLASSNAME,
+ IPC_TRANSACTIONSERVICE_CID,
+ IPC_TRANSACTIONSERVICE_CONTRACTID,
+ tmTransactionServiceConstructor },
+
+#ifdef BUILD_DCONNECT
+ { IPC_DCONNECTSERVICE_CLASSNAME,
+ IPC_DCONNECTSERVICE_CID,
+ IPC_DCONNECTSERVICE_CONTRACTID,
+ ipcDConnectServiceConstructor,
+ ipcDConnectServiceRegisterProc,
+ ipcDConnectServiceUnregisterProc },
+#endif
+};
+
+//-----------------------------------------------------------------------------
+
+PR_STATIC_CALLBACK(nsresult)
+ipcdclient_init(nsIModule *module)
+{
+ return IPC_Init();
+}
+
+PR_STATIC_CALLBACK(void)
+ipcdclient_shutdown(nsIModule *module)
+{
+ IPC_Shutdown();
+}
+
+//-----------------------------------------------------------------------------
+// Implement the NSGetModule() exported function for your module
+// and the entire implementation of the module object.
+//-----------------------------------------------------------------------------
+NS_IMPL_NSGETMODULE_WITH_CTOR_DTOR(ipcdclient, components,
+ ipcdclient_init,
+ ipcdclient_shutdown)
diff --git a/src/libs/xpcom18a4/ipc/ipcd/client/src/ipcService.cpp b/src/libs/xpcom18a4/ipc/ipcd/client/src/ipcService.cpp
new file mode 100644
index 00000000..6c2a1326
--- /dev/null
+++ b/src/libs/xpcom18a4/ipc/ipcd/client/src/ipcService.cpp
@@ -0,0 +1,120 @@
+/* vim:set ts=4 sw=4 et cindent: */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Mozilla IPC.
+ *
+ * The Initial Developer of the Original Code is IBM Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 2004
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Darin Fisher <darin@meer.net>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#include "ipcService.h"
+
+// The ipcService implementation is nothing more than a thin XPCOM wrapper
+// around the ipcdclient.h API.
+
+NS_IMPL_THREADSAFE_ISUPPORTS1(ipcService, ipcIService)
+
+NS_IMETHODIMP
+ipcService::GetID(PRUint32 *aID)
+{
+ return IPC_GetID(aID);
+}
+
+NS_IMETHODIMP
+ipcService::AddName(const char *aName)
+{
+ return IPC_AddName(aName);
+}
+
+NS_IMETHODIMP
+ipcService::RemoveName(const char *aName)
+{
+ return IPC_RemoveName(aName);
+}
+
+NS_IMETHODIMP
+ipcService::AddClientObserver(ipcIClientObserver *aObserver)
+{
+ return IPC_AddClientObserver(aObserver);
+}
+
+NS_IMETHODIMP
+ipcService::RemoveClientObserver(ipcIClientObserver *aObserver)
+{
+ return IPC_RemoveClientObserver(aObserver);
+}
+
+NS_IMETHODIMP
+ipcService::ResolveClientName(const char *aName, PRUint32 *aID)
+{
+ return IPC_ResolveClientName(aName, aID);
+}
+
+NS_IMETHODIMP
+ipcService::ClientExists(PRUint32 aClientID, PRBool *aResult)
+{
+ return IPC_ClientExists(aClientID, aResult);
+}
+
+NS_IMETHODIMP
+ipcService::DefineTarget(const nsID &aTarget, ipcIMessageObserver *aObserver,
+ PRBool aOnCurrentThread)
+{
+ return IPC_DefineTarget(aTarget, aObserver, aOnCurrentThread);
+}
+
+NS_IMETHODIMP
+ipcService::SendMessage(PRUint32 aReceiverID, const nsID &aTarget,
+ const PRUint8 *aData, PRUint32 aDataLen)
+{
+ return IPC_SendMessage(aReceiverID, aTarget, aData, aDataLen);
+}
+
+NS_IMETHODIMP
+ipcService::WaitMessage(PRUint32 aSenderID, const nsID &aTarget,
+ ipcIMessageObserver *aObserver,
+ PRUint32 aTimeout)
+{
+ return IPC_WaitMessage(aSenderID, aTarget, aObserver, nsnull,
+ PR_MillisecondsToInterval(aTimeout));
+}
+
+NS_IMETHODIMP
+ipcService::DisableMessageObserver(const nsID &aTarget)
+{
+ return IPC_DisableMessageObserver(aTarget);
+}
+
+NS_IMETHODIMP
+ipcService::EnableMessageObserver(const nsID &aTarget)
+{
+ return IPC_EnableMessageObserver(aTarget);
+}
diff --git a/src/libs/xpcom18a4/ipc/ipcd/client/src/ipcService.h b/src/libs/xpcom18a4/ipc/ipcd/client/src/ipcService.h
new file mode 100644
index 00000000..80f953cf
--- /dev/null
+++ b/src/libs/xpcom18a4/ipc/ipcd/client/src/ipcService.h
@@ -0,0 +1,51 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Mozilla IPC.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 2002
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Darin Fisher <darin@netscape.com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#ifndef ipcService_h__
+#define ipcService_h__
+
+#include "ipcIService.h"
+#include "ipcdclient.h"
+
+class ipcService : public ipcIService
+{
+public:
+ NS_DECL_ISUPPORTS
+ NS_DECL_IPCISERVICE
+};
+
+#endif // !defined( ipcService_h__ )
diff --git a/src/libs/xpcom18a4/ipc/ipcd/client/src/ipcdclient.cpp b/src/libs/xpcom18a4/ipc/ipcd/client/src/ipcdclient.cpp
new file mode 100644
index 00000000..e5527757
--- /dev/null
+++ b/src/libs/xpcom18a4/ipc/ipcd/client/src/ipcdclient.cpp
@@ -0,0 +1,1490 @@
+/* vim:set ts=2 sw=2 et cindent: */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Mozilla IPC.
+ *
+ * The Initial Developer of the Original Code is IBM Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 2004
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Darin Fisher <darin@meer.net>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#include "ipcdclient.h"
+#include "ipcConnection.h"
+#include "ipcConfig.h"
+#include "ipcMessageQ.h"
+#include "ipcMessageUtils.h"
+#include "ipcLog.h"
+#include "ipcm.h"
+
+#include "nsIFile.h"
+#include "nsEventQueueUtils.h"
+#include "nsDirectoryServiceUtils.h"
+#include "nsDirectoryServiceDefs.h"
+#include "nsCOMPtr.h"
+#include "nsHashKeys.h"
+#include "nsRefPtrHashtable.h"
+#include "nsAutoLock.h"
+#include "nsProxyRelease.h"
+#include "nsCOMArray.h"
+
+#include "prio.h"
+#include "prproces.h"
+#include "pratom.h"
+
+#ifdef VBOX
+# include <iprt/critsect.h>
+# define VBOX_WITH_IPCCLIENT_RW_CS
+#endif
+
+/* ------------------------------------------------------------------------- */
+
+#define IPC_REQUEST_TIMEOUT PR_SecondsToInterval(30)
+
+/* ------------------------------------------------------------------------- */
+
+class ipcTargetData
+{
+public:
+ static NS_HIDDEN_(ipcTargetData*) Create();
+
+ // threadsafe addref/release
+ NS_HIDDEN_(nsrefcnt) AddRef() { return PR_AtomicIncrement(&refcnt); }
+ NS_HIDDEN_(nsrefcnt) Release() { PRInt32 r = PR_AtomicDecrement(&refcnt); if (r == 0) delete this; return r; }
+
+ NS_HIDDEN_(void) SetObserver(ipcIMessageObserver *aObserver, PRBool aOnCurrentThread);
+
+ // protects access to the members of this class
+ PRMonitor *monitor;
+
+ // this may be null
+ nsCOMPtr<ipcIMessageObserver> observer;
+
+ // the message observer is called via this event queue
+ nsCOMPtr<nsIEventQueue> eventQ;
+
+ // incoming messages are added to this list
+ ipcMessageQ pendingQ;
+
+ // non-zero if the observer has been disabled (this means that new messages
+ // should not be dispatched to the observer until the observer is re-enabled
+ // via IPC_EnableMessageObserver).
+ PRInt32 observerDisabled;
+
+private:
+
+ ipcTargetData()
+ : monitor(nsAutoMonitor::NewMonitor("ipcTargetData"))
+ , observerDisabled(0)
+ , refcnt(0)
+ {}
+
+ ~ipcTargetData()
+ {
+ if (monitor)
+ nsAutoMonitor::DestroyMonitor(monitor);
+ }
+
+ PRInt32 refcnt;
+};
+
+ipcTargetData *
+ipcTargetData::Create()
+{
+ ipcTargetData *td = new ipcTargetData;
+ if (!td)
+ return NULL;
+
+ if (!td->monitor)
+ {
+ delete td;
+ return NULL;
+ }
+ return td;
+}
+
+void
+ipcTargetData::SetObserver(ipcIMessageObserver *aObserver, PRBool aOnCurrentThread)
+{
+ observer = aObserver;
+
+ if (aOnCurrentThread)
+ NS_GetCurrentEventQ(getter_AddRefs(eventQ));
+ else
+ eventQ = nsnull;
+}
+
+/* ------------------------------------------------------------------------- */
+
+typedef nsRefPtrHashtable<nsIDHashKey, ipcTargetData> ipcTargetMap;
+
+class ipcClientState
+{
+public:
+ static NS_HIDDEN_(ipcClientState *) Create();
+
+ ~ipcClientState()
+ {
+#ifndef VBOX_WITH_IPCCLIENT_RW_CS
+ if (monitor)
+ nsAutoMonitor::DestroyMonitor(monitor);
+#else
+ RTCritSectRwDelete(&critSect);
+#endif
+ }
+
+#ifndef VBOX_WITH_IPCCLIENT_RW_CS
+ //
+ // the monitor protects the targetMap and the connected and shutdown flags.
+ //
+ // NOTE: we use a PRMonitor for this instead of a PRLock because we need
+ // the lock to be re-entrant. since we don't ever need to wait on
+ // this monitor, it might be worth it to implement a re-entrant
+ // wrapper for PRLock.
+ //
+ PRMonitor *monitor;
+#else /* VBOX_WITH_IPCCLIENT_RW_CS */
+ RTCRITSECTRW critSect;
+#endif /* VBOX_WITH_IPCCLIENT_RW_CS */
+ ipcTargetMap targetMap;
+ PRBool connected;
+ PRBool shutdown;
+
+ // our process's client id
+ PRUint32 selfID;
+
+ nsCOMArray<ipcIClientObserver> clientObservers;
+
+private:
+
+ ipcClientState()
+#ifndef VBOX_WITH_IPCCLIENT_RW_CS
+ : monitor(nsAutoMonitor::NewMonitor("ipcClientState"))
+ , connected(PR_FALSE)
+#else
+ : connected(PR_FALSE)
+#endif
+ , shutdown(PR_FALSE)
+ , selfID(0)
+ {
+#ifdef VBOX_WITH_IPCCLIENT_RW_CS
+ /* Not employing the lock validator here to keep performance up in debug builds. */
+ RTCritSectRwInitEx(&critSect, RTCRITSECT_FLAGS_NO_LOCK_VAL, NIL_RTLOCKVALCLASS, RTLOCKVAL_SUB_CLASS_NONE, NULL);
+#endif
+ }
+};
+
+ipcClientState *
+ipcClientState::Create()
+{
+ ipcClientState *cs = new ipcClientState;
+ if (!cs)
+ return NULL;
+
+#ifndef VBOX_WITH_IPCCLIENT_RW_CS
+ if (!cs->monitor || !cs->targetMap.Init())
+#else
+ if (!RTCritSectRwIsInitialized(&cs->critSect) || !cs->targetMap.Init())
+#endif
+ {
+ delete cs;
+ return NULL;
+ }
+
+ return cs;
+}
+
+/* ------------------------------------------------------------------------- */
+
+static ipcClientState *gClientState;
+
+static PRBool
+GetTarget(const nsID &aTarget, ipcTargetData **td)
+{
+#ifndef VBOX_WITH_IPCCLIENT_RW_CS
+ nsAutoMonitor mon(gClientState->monitor);
+ return gClientState->targetMap.Get(nsIDHashKey(&aTarget).GetKey(), td);
+#else
+ RTCritSectRwEnterShared(&gClientState->critSect);
+ PRBool fRc = gClientState->targetMap.Get(nsIDHashKey(&aTarget).GetKey(), td);
+ RTCritSectRwLeaveShared(&gClientState->critSect);
+ return fRc;
+#endif
+}
+
+static PRBool
+PutTarget(const nsID &aTarget, ipcTargetData *td)
+{
+#ifndef VBOX_WITH_IPCCLIENT_RW_CS
+ nsAutoMonitor mon(gClientState->monitor);
+ return gClientState->targetMap.Put(nsIDHashKey(&aTarget).GetKey(), td);
+#else
+ RTCritSectRwEnterExcl(&gClientState->critSect);
+ PRBool fRc = gClientState->targetMap.Put(nsIDHashKey(&aTarget).GetKey(), td);
+ RTCritSectRwLeaveExcl(&gClientState->critSect);
+ return fRc;
+#endif
+}
+
+static void
+DelTarget(const nsID &aTarget)
+{
+#ifndef VBOX_WITH_IPCCLIENT_RW_CS
+ nsAutoMonitor mon(gClientState->monitor);
+ gClientState->targetMap.Remove(nsIDHashKey(&aTarget).GetKey());
+#else
+ RTCritSectRwEnterExcl(&gClientState->critSect);
+ gClientState->targetMap.Remove(nsIDHashKey(&aTarget).GetKey());
+ RTCritSectRwLeaveExcl(&gClientState->critSect);
+#endif
+}
+
+/* ------------------------------------------------------------------------- */
+
+static nsresult
+GetDaemonPath(nsCString &dpath)
+{
+ nsCOMPtr<nsIFile> file;
+
+ nsresult rv = NS_GetSpecialDirectory(NS_XPCOM_CURRENT_PROCESS_DIR,
+ getter_AddRefs(file));
+ if (NS_SUCCEEDED(rv))
+ {
+ rv = file->AppendNative(NS_LITERAL_CSTRING(IPC_DAEMON_APP_NAME));
+ if (NS_SUCCEEDED(rv))
+ rv = file->GetNativePath(dpath);
+ }
+
+ return rv;
+}
+
+/* ------------------------------------------------------------------------- */
+
+static void
+ProcessPendingQ(const nsID &aTarget)
+{
+ ipcMessageQ tempQ;
+
+ nsRefPtr<ipcTargetData> td;
+ if (GetTarget(aTarget, getter_AddRefs(td)))
+ {
+ nsAutoMonitor mon(td->monitor);
+
+ // if the observer for this target has been temporarily disabled, then
+ // we must not processing any pending messages at this time.
+
+ if (!td->observerDisabled)
+ td->pendingQ.MoveTo(tempQ);
+ }
+
+ // process pending queue outside monitor
+ while (!tempQ.IsEmpty())
+ {
+ ipcMessage *msg = tempQ.First();
+
+ // it is possible that messages for other targets are in the queue
+ // (currently, this can be only a IPCM_MSG_PSH_CLIENT_STATE message
+ // initially addressed to IPCM_TARGET, see IPC_OnMessageAvailable())
+ // --ignore them.
+ if (td->observer && msg->Target().Equals(aTarget))
+ td->observer->OnMessageAvailable(msg->mMetaData,
+ msg->Target(),
+ (const PRUint8 *) msg->Data(),
+ msg->DataLen());
+ else
+ {
+ // the IPCM target does not have an observer, and therefore any IPCM
+ // messages that make it here will simply be dropped.
+ NS_ASSERTION(aTarget.Equals(IPCM_TARGET) || msg->Target().Equals(IPCM_TARGET),
+ "unexpected target");
+ LOG(("dropping IPCM message: type=%x\n", IPCM_GetType(msg)));
+ }
+ tempQ.DeleteFirst();
+ }
+}
+
+/* ------------------------------------------------------------------------- */
+
+// WaitTarget enables support for multiple threads blocking on the same
+// message target. the selector is called while inside the target's monitor.
+
+typedef nsresult (* ipcMessageSelector)(
+ void *arg,
+ ipcTargetData *td,
+ const ipcMessage *msg
+);
+
+// selects any
+static nsresult
+DefaultSelector(void *arg, ipcTargetData *td, const ipcMessage *msg)
+{
+ return NS_OK;
+}
+
+static nsresult
+WaitTarget(const nsID &aTarget,
+ PRIntervalTime aTimeout,
+ ipcMessage **aMsg,
+ ipcMessageSelector aSelector = nsnull,
+ void *aArg = nsnull)
+{
+ *aMsg = nsnull;
+
+ if (!aSelector)
+ aSelector = DefaultSelector;
+
+ nsRefPtr<ipcTargetData> td;
+ if (!GetTarget(aTarget, getter_AddRefs(td)))
+ return NS_ERROR_INVALID_ARG; // bad aTarget
+
+ PRBool isIPCMTarget = aTarget.Equals(IPCM_TARGET);
+
+ PRIntervalTime timeStart = PR_IntervalNow();
+ PRIntervalTime timeEnd;
+ if (aTimeout == PR_INTERVAL_NO_TIMEOUT)
+ timeEnd = aTimeout;
+ else if (aTimeout == PR_INTERVAL_NO_WAIT)
+ timeEnd = timeStart;
+ else
+ {
+ timeEnd = timeStart + aTimeout;
+
+ // if overflowed, then set to max value
+ if (timeEnd < timeStart)
+ timeEnd = PR_INTERVAL_NO_TIMEOUT;
+ }
+
+ ipcMessage *lastChecked = nsnull, *beforeLastChecked = nsnull;
+ nsresult rv = NS_ERROR_ABORT;
+
+ nsAutoMonitor mon(td->monitor);
+
+ // only the ICPM target is allowed to wait for a message after shutdown
+ // (but before disconnection). this gives client observers called from
+ // IPC_Shutdown a chance to use IPC_SendMessage to send necessary
+ // "last minute" messages to other clients.
+
+ while (gClientState->connected && (!gClientState->shutdown || isIPCMTarget))
+ {
+ NS_ASSERTION(!lastChecked, "oops");
+
+ //
+ // NOTE:
+ //
+ // we must start at the top of the pending queue, possibly revisiting
+ // messages that our selector has already rejected. this is necessary
+ // because the queue may have been modified while we were waiting on
+ // the monitor. the impact of this on performance remains to be seen.
+ //
+ // one cheap solution is to keep a counter that is incremented each
+ // time a message is removed from the pending queue. that way we can
+ // avoid revisiting all messages sometimes.
+ //
+
+ lastChecked = td->pendingQ.First();
+ beforeLastChecked = nsnull;
+
+ // loop over pending queue until we find a message that our selector likes.
+ while (lastChecked)
+ {
+ //
+ // it is possible that this call to WaitTarget() has been initiated by
+ // some other selector, that might be currently processing the same
+ // message (since the message remains in the queue until the selector
+ // returns TRUE). here we prevent this situation by using a special flag
+ // to guarantee that every message is processed only once.
+ //
+
+ if (!lastChecked->TestFlag(IPC_MSG_FLAG_IN_PROCESS))
+ {
+ lastChecked->SetFlag(IPC_MSG_FLAG_IN_PROCESS);
+ nsresult acceptedRV = (aSelector)(aArg, td, lastChecked);
+ lastChecked->ClearFlag(IPC_MSG_FLAG_IN_PROCESS);
+
+ if (acceptedRV != IPC_WAIT_NEXT_MESSAGE)
+ {
+ if (acceptedRV == NS_OK)
+ {
+ // remove from pending queue
+ if (beforeLastChecked)
+ td->pendingQ.RemoveAfter(beforeLastChecked);
+ else
+ td->pendingQ.RemoveFirst();
+
+ lastChecked->mNext = nsnull;
+ *aMsg = lastChecked;
+ break;
+ }
+ else /* acceptedRV == IPC_DISCARD_MESSAGE */
+ {
+ ipcMessage *nextToCheck = lastChecked->mNext;
+
+ // discard from pending queue
+ if (beforeLastChecked)
+ td->pendingQ.DeleteAfter(beforeLastChecked);
+ else
+ td->pendingQ.DeleteFirst();
+
+ lastChecked = nextToCheck;
+
+ continue;
+ }
+ }
+ }
+
+ beforeLastChecked = lastChecked;
+ lastChecked = lastChecked->mNext;
+ }
+
+ if (*aMsg)
+ {
+ rv = NS_OK;
+ break;
+ }
+#ifdef VBOX
+ else
+ {
+ /* Special client liveness check if there is no message to process.
+ * This is necessary as there might be several threads waiting for
+ * a message from a single client, and only one gets the DOWN msg. */
+ nsresult aliveRV = (aSelector)(aArg, td, NULL);
+ if (aliveRV != IPC_WAIT_NEXT_MESSAGE)
+ {
+ *aMsg = NULL;
+ break;
+ }
+ }
+#endif /* VBOX */
+
+ PRIntervalTime t = PR_IntervalNow();
+ if (t > timeEnd) // check if timeout has expired
+ {
+ rv = IPC_ERROR_WOULD_BLOCK;
+ break;
+ }
+ mon.Wait(timeEnd - t);
+
+ LOG(("woke up from sleep [pendingQempty=%d connected=%d shutdown=%d isIPCMTarget=%d]\n",
+ td->pendingQ.IsEmpty(), gClientState->connected,
+ gClientState->shutdown, isIPCMTarget));
+ }
+
+ return rv;
+}
+
+/* ------------------------------------------------------------------------- */
+
+static void
+PostEvent(nsIEventTarget *eventTarget, PLEvent *ev)
+{
+ if (!ev)
+ return;
+
+ nsresult rv = eventTarget->PostEvent(ev);
+ if (NS_FAILED(rv))
+ {
+ NS_WARNING("PostEvent failed");
+ PL_DestroyEvent(ev);
+ }
+}
+
+static void
+PostEventToMainThread(PLEvent *ev)
+{
+ nsCOMPtr<nsIEventQueue> eventQ;
+ NS_GetMainEventQ(getter_AddRefs(eventQ));
+ if (!eventQ)
+ {
+ NS_WARNING("unable to get reference to main event queue");
+ PL_DestroyEvent(ev);
+ return;
+ }
+ PostEvent(eventQ, ev);
+}
+
+/* ------------------------------------------------------------------------- */
+
+class ipcEvent_ClientState : public PLEvent
+{
+public:
+ ipcEvent_ClientState(PRUint32 aClientID, PRUint32 aClientState)
+ : mClientID(aClientID)
+ , mClientState(aClientState)
+ {
+ PL_InitEvent(this, nsnull, HandleEvent, DestroyEvent);
+ }
+
+ PR_STATIC_CALLBACK(void *) HandleEvent(PLEvent *ev)
+ {
+ // maybe we've been shutdown!
+ if (!gClientState)
+ return nsnull;
+
+ ipcEvent_ClientState *self = (ipcEvent_ClientState *) ev;
+
+ for (PRInt32 i=0; i<gClientState->clientObservers.Count(); ++i)
+ gClientState->clientObservers[i]->OnClientStateChange(self->mClientID,
+ self->mClientState);
+ return nsnull;
+ }
+
+ PR_STATIC_CALLBACK(void) DestroyEvent(PLEvent *ev)
+ {
+ delete (ipcEvent_ClientState *) ev;
+ }
+
+private:
+ PRUint32 mClientID;
+ PRUint32 mClientState;
+};
+
+/* ------------------------------------------------------------------------- */
+
+class ipcEvent_ProcessPendingQ : public PLEvent
+{
+public:
+ ipcEvent_ProcessPendingQ(const nsID &aTarget)
+ : mTarget(aTarget)
+ {
+ PL_InitEvent(this, nsnull, HandleEvent, DestroyEvent);
+ }
+
+ PR_STATIC_CALLBACK(void *) HandleEvent(PLEvent *ev)
+ {
+ ProcessPendingQ(((ipcEvent_ProcessPendingQ *) ev)->mTarget);
+ return nsnull;
+ }
+
+ PR_STATIC_CALLBACK(void) DestroyEvent(PLEvent *ev)
+ {
+ delete (ipcEvent_ProcessPendingQ *) ev;
+ }
+
+private:
+ const nsID mTarget;
+};
+
+static void
+CallProcessPendingQ(const nsID &target, ipcTargetData *td)
+{
+ // we assume that we are inside td's monitor
+
+ PLEvent *ev = new ipcEvent_ProcessPendingQ(target);
+ if (!ev)
+ return;
+
+ nsresult rv;
+
+ if (td->eventQ)
+ rv = td->eventQ->PostEvent(ev);
+ else
+ rv = IPC_DoCallback((ipcCallbackFunc) PL_HandleEvent, ev);
+
+ if (NS_FAILED(rv))
+ PL_DestroyEvent(ev);
+}
+
+/* ------------------------------------------------------------------------- */
+
+static void
+DisableMessageObserver(const nsID &aTarget)
+{
+ nsRefPtr<ipcTargetData> td;
+ if (GetTarget(aTarget, getter_AddRefs(td)))
+ {
+ nsAutoMonitor mon(td->monitor);
+ ++td->observerDisabled;
+ }
+}
+
+static void
+EnableMessageObserver(const nsID &aTarget)
+{
+ nsRefPtr<ipcTargetData> td;
+ if (GetTarget(aTarget, getter_AddRefs(td)))
+ {
+ nsAutoMonitor mon(td->monitor);
+ if (td->observerDisabled > 0 && --td->observerDisabled == 0)
+ if (!td->pendingQ.IsEmpty())
+ CallProcessPendingQ(aTarget, td);
+ }
+}
+
+/* ------------------------------------------------------------------------- */
+
+// converts IPCM_ERROR_* status codes to NS_ERROR_* status codes
+static nsresult nsresult_from_ipcm_result(PRInt32 status)
+{
+ nsresult rv = NS_ERROR_FAILURE;
+
+ switch (status)
+ {
+ case IPCM_ERROR_GENERIC: rv = NS_ERROR_FAILURE; break;
+ case IPCM_ERROR_INVALID_ARG: rv = NS_ERROR_INVALID_ARG; break;
+ case IPCM_ERROR_NO_CLIENT: rv = NS_ERROR_CALL_FAILED; break;
+ // TODO: select better mapping for the below codes
+ case IPCM_ERROR_NO_SUCH_DATA:
+ case IPCM_ERROR_ALREADY_EXISTS: rv = NS_ERROR_FAILURE; break;
+ default: NS_ASSERTION(PR_FALSE, "No conversion");
+ }
+
+ return rv;
+}
+
+/* ------------------------------------------------------------------------- */
+
+// selects the next IPCM message with matching request index
+static nsresult
+WaitIPCMResponseSelector(void *arg, ipcTargetData *td, const ipcMessage *msg)
+{
+#ifdef VBOX
+ if (!msg)
+ return IPC_WAIT_NEXT_MESSAGE;
+#endif /* VBOX */
+ PRUint32 requestIndex = *(PRUint32 *) arg;
+ return IPCM_GetRequestIndex(msg) == requestIndex ? NS_OK : IPC_WAIT_NEXT_MESSAGE;
+}
+
+// wait for an IPCM response message. if responseMsg is null, then it is
+// assumed that the caller does not care to get a reference to the
+// response itself. if the response is an IPCM_MSG_ACK_RESULT, then the
+// status code is mapped to a nsresult and returned by this function.
+static nsresult
+WaitIPCMResponse(PRUint32 requestIndex, ipcMessage **responseMsg = nsnull)
+{
+ ipcMessage *msg;
+
+ nsresult rv = WaitTarget(IPCM_TARGET, IPC_REQUEST_TIMEOUT, &msg,
+ WaitIPCMResponseSelector, &requestIndex);
+ if (NS_FAILED(rv))
+ return rv;
+
+ if (IPCM_GetType(msg) == IPCM_MSG_ACK_RESULT)
+ {
+ ipcMessageCast<ipcmMessageResult> result(msg);
+ if (result->Status() < 0)
+ rv = nsresult_from_ipcm_result(result->Status());
+ else
+ rv = NS_OK;
+ }
+
+ if (responseMsg)
+ *responseMsg = msg;
+ else
+ delete msg;
+
+ return rv;
+}
+
+// make an IPCM request and wait for a response.
+static nsresult
+MakeIPCMRequest(ipcMessage *msg, ipcMessage **responseMsg = nsnull)
+{
+ if (!msg)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ PRUint32 requestIndex = IPCM_GetRequestIndex(msg);
+
+ // suppress 'ProcessPendingQ' for IPCM messages until we receive the
+ // response to this IPCM request. if we did not do this then there
+ // would be a race condition leading to the possible removal of our
+ // response from the pendingQ between sending the request and waiting
+ // for the response.
+ DisableMessageObserver(IPCM_TARGET);
+
+ nsresult rv = IPC_SendMsg(msg);
+ if (NS_SUCCEEDED(rv))
+ rv = WaitIPCMResponse(requestIndex, responseMsg);
+
+ EnableMessageObserver(IPCM_TARGET);
+ return rv;
+}
+
+/* ------------------------------------------------------------------------- */
+
+static void
+RemoveTarget(const nsID &aTarget, PRBool aNotifyDaemon)
+{
+ DelTarget(aTarget);
+
+ if (aNotifyDaemon)
+ {
+ nsresult rv = MakeIPCMRequest(new ipcmMessageClientDelTarget(aTarget));
+ if (NS_FAILED(rv))
+ LOG(("failed to delete target: rv=%x\n", rv));
+ }
+}
+
+static nsresult
+DefineTarget(const nsID &aTarget,
+ ipcIMessageObserver *aObserver,
+ PRBool aOnCurrentThread,
+ PRBool aNotifyDaemon,
+ ipcTargetData **aResult)
+{
+ nsresult rv;
+
+ nsRefPtr<ipcTargetData> td( ipcTargetData::Create() );
+ if (!td)
+ return NS_ERROR_OUT_OF_MEMORY;
+ td->SetObserver(aObserver, aOnCurrentThread);
+
+ if (!PutTarget(aTarget, td))
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ if (aNotifyDaemon)
+ {
+ rv = MakeIPCMRequest(new ipcmMessageClientAddTarget(aTarget));
+ if (NS_FAILED(rv))
+ {
+ LOG(("failed to add target: rv=%x\n", rv));
+ RemoveTarget(aTarget, PR_FALSE);
+ return rv;
+ }
+ }
+
+ if (aResult)
+ NS_ADDREF(*aResult = td);
+ return NS_OK;
+}
+
+/* ------------------------------------------------------------------------- */
+
+static nsresult
+TryConnect()
+{
+ nsCAutoString dpath;
+ nsresult rv = GetDaemonPath(dpath);
+ if (NS_FAILED(rv))
+ return rv;
+
+ rv = IPC_Connect(dpath.get());
+ if (NS_FAILED(rv))
+ return rv;
+
+ gClientState->connected = PR_TRUE;
+
+ rv = DefineTarget(IPCM_TARGET, nsnull, PR_FALSE, PR_FALSE, nsnull);
+ if (NS_FAILED(rv))
+ return rv;
+
+ ipcMessage *msg;
+
+ // send CLIENT_HELLO and wait for CLIENT_ID response...
+ rv = MakeIPCMRequest(new ipcmMessageClientHello(), &msg);
+ if (NS_FAILED(rv))
+ return rv;
+
+ if (IPCM_GetType(msg) == IPCM_MSG_ACK_CLIENT_ID)
+ gClientState->selfID = ipcMessageCast<ipcmMessageClientID>(msg)->ClientID();
+ else
+ {
+ LOG(("unexpected response from CLIENT_HELLO message: type=%x!\n",
+ IPCM_GetType(msg)));
+ rv = NS_ERROR_UNEXPECTED;
+ }
+
+ delete msg;
+ return rv;
+}
+
+nsresult
+IPC_Init()
+{
+ NS_ENSURE_TRUE(!gClientState, NS_ERROR_ALREADY_INITIALIZED);
+
+ IPC_InitLog(">>>");
+
+ gClientState = ipcClientState::Create();
+ if (!gClientState)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ nsresult rv = TryConnect();
+ if (NS_FAILED(rv))
+ IPC_Shutdown();
+
+ return rv;
+}
+
+PR_STATIC_CALLBACK(PLDHashOperator)
+EnumerateTargetMapAndNotify(const nsID &aKey,
+ ipcTargetData *aData,
+ void *aClosure);
+
+nsresult
+IPC_Shutdown()
+{
+ NS_ENSURE_TRUE(gClientState, NS_ERROR_NOT_INITIALIZED);
+
+ LOG(("IPC_Shutdown: connected=%d\n",gClientState->connected));
+
+ if (gClientState->connected)
+ {
+ {
+ // first, set the shutdown flag and unblock any calls to WaitTarget.
+ // all targets but IPCM will not be able to use WaitTarget any more.
+
+#ifndef VBOX_WITH_IPCCLIENT_RW_CS
+ nsAutoMonitor mon(gClientState->monitor);
+#else
+ RTCritSectRwEnterExcl(&gClientState->critSect);
+#endif
+
+ gClientState->shutdown = PR_TRUE;
+ gClientState->targetMap.EnumerateRead(EnumerateTargetMapAndNotify, nsnull);
+
+#ifdef VBOX_WITH_IPCCLIENT_RW_CS
+ RTCritSectRwLeaveExcl(&gClientState->critSect);
+#endif
+ }
+
+ // inform all client observers that we're being shutdown to let interested
+ // parties gracefully uninitialize themselves. the IPCM target is still
+ // fully operational at this point, so they can use IPC_SendMessage
+ // (this is essential for the DConnect extension, for example, to do the
+ // proper uninitialization).
+
+ ipcEvent_ClientState *ev = new ipcEvent_ClientState(IPC_SENDER_ANY,
+ IPCM_CLIENT_STATE_DOWN);
+ ipcEvent_ClientState::HandleEvent (ev);
+ ipcEvent_ClientState::DestroyEvent (ev);
+
+ IPC_Disconnect();
+ }
+
+ //
+ // make gClientState nsnull before deletion to cause all public IPC_*
+ // calls (possibly made during ipcClientState destruction) to return
+ // NS_ERROR_NOT_INITIALIZED.
+ //
+ // NOTE: isn't just checking for gClientState->connected in every appropriate
+ // IPC_* method a better solution?
+ //
+ ipcClientState *aClientState = gClientState;
+ gClientState = nsnull;
+ delete aClientState;
+
+ return NS_OK;
+}
+
+/* ------------------------------------------------------------------------- */
+
+nsresult
+IPC_DefineTarget(const nsID &aTarget,
+ ipcIMessageObserver *aObserver,
+ PRBool aOnCurrentThread)
+{
+ NS_ENSURE_TRUE(gClientState, NS_ERROR_NOT_INITIALIZED);
+
+ // do not permit the re-definition of the IPCM protocol's target.
+ if (aTarget.Equals(IPCM_TARGET))
+ return NS_ERROR_INVALID_ARG;
+
+ nsresult rv;
+
+ nsRefPtr<ipcTargetData> td;
+ if (GetTarget(aTarget, getter_AddRefs(td)))
+ {
+ // clear out observer before removing target since we want to ensure that
+ // the observer is released on the main thread.
+ {
+ nsAutoMonitor mon(td->monitor);
+ td->SetObserver(aObserver, aOnCurrentThread);
+ }
+
+ // remove target outside of td's monitor to avoid holding the monitor
+ // while entering the client state's monitor.
+ if (!aObserver)
+ RemoveTarget(aTarget, PR_TRUE);
+
+ rv = NS_OK;
+ }
+ else
+ {
+ if (aObserver)
+ rv = DefineTarget(aTarget, aObserver, aOnCurrentThread, PR_TRUE, nsnull);
+ else
+ rv = NS_ERROR_INVALID_ARG; // unknown target
+ }
+
+ return rv;
+}
+
+nsresult
+IPC_DisableMessageObserver(const nsID &aTarget)
+{
+ NS_ENSURE_TRUE(gClientState, NS_ERROR_NOT_INITIALIZED);
+
+ // do not permit modifications to the IPCM protocol's target.
+ if (aTarget.Equals(IPCM_TARGET))
+ return NS_ERROR_INVALID_ARG;
+
+ DisableMessageObserver(aTarget);
+ return NS_OK;
+}
+
+nsresult
+IPC_EnableMessageObserver(const nsID &aTarget)
+{
+ NS_ENSURE_TRUE(gClientState, NS_ERROR_NOT_INITIALIZED);
+
+ // do not permit modifications to the IPCM protocol's target.
+ if (aTarget.Equals(IPCM_TARGET))
+ return NS_ERROR_INVALID_ARG;
+
+ EnableMessageObserver(aTarget);
+ return NS_OK;
+}
+
+nsresult
+IPC_SendMessage(PRUint32 aReceiverID,
+ const nsID &aTarget,
+ const PRUint8 *aData,
+ PRUint32 aDataLen)
+{
+ NS_ENSURE_TRUE(gClientState, NS_ERROR_NOT_INITIALIZED);
+
+ // do not permit sending IPCM messages
+ if (aTarget.Equals(IPCM_TARGET))
+ return NS_ERROR_INVALID_ARG;
+
+ nsresult rv;
+ if (aReceiverID == 0)
+ {
+ ipcMessage *msg = new ipcMessage(aTarget, (const char *) aData, aDataLen);
+ if (!msg)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ rv = IPC_SendMsg(msg);
+ }
+ else
+ rv = MakeIPCMRequest(new ipcmMessageForward(IPCM_MSG_REQ_FORWARD,
+ aReceiverID,
+ aTarget,
+ (const char *) aData,
+ aDataLen));
+
+ return rv;
+}
+
+struct WaitMessageSelectorData
+{
+ PRUint32 senderID;
+ ipcIMessageObserver *observer;
+ PRBool senderDead;
+};
+
+static nsresult WaitMessageSelector(void *arg, ipcTargetData *td, const ipcMessage *msg)
+{
+ WaitMessageSelectorData *data = (WaitMessageSelectorData *) arg;
+#ifdef VBOX
+ if (!msg)
+ {
+ /* Special NULL message which asks to check whether the client is
+ * still alive. Called when there is nothing suitable in the queue. */
+ ipcIMessageObserver *obs = data->observer;
+ if (!obs)
+ obs = td->observer;
+ NS_ASSERTION(obs, "must at least have a default observer");
+
+ nsresult rv = obs->OnMessageAvailable(IPC_SENDER_ANY, nsID(), 0, 0);
+ if (rv != IPC_WAIT_NEXT_MESSAGE)
+ data->senderDead = PR_TRUE;
+
+ return rv;
+ }
+#endif /* VBOX */
+
+ // process the specially forwarded client state message to see if the
+ // sender we're waiting a message from has died.
+
+ if (msg->Target().Equals(IPCM_TARGET))
+ {
+ switch (IPCM_GetType(msg))
+ {
+ case IPCM_MSG_PSH_CLIENT_STATE:
+ {
+ ipcMessageCast<ipcmMessageClientState> status(msg);
+ if ((data->senderID == IPC_SENDER_ANY ||
+ status->ClientID() == data->senderID) &&
+ status->ClientState() == IPCM_CLIENT_STATE_DOWN)
+ {
+ LOG(("sender (%d) we're waiting a message from (%d) has died\n",
+ status->ClientID(), data->senderID));
+
+ if (data->senderID != IPC_SENDER_ANY)
+ {
+ // we're waiting on a particular client, so IPC_WaitMessage must
+ // definitely fail with the NS_ERROR_xxx result.
+
+ data->senderDead = PR_TRUE;
+ return IPC_DISCARD_MESSAGE; // consume the message
+ }
+ else
+ {
+ // otherwise inform the observer about the client death using a special
+ // null message with an empty target id, and fail IPC_WaitMessage call
+ // with NS_ERROR_xxx only if the observer accepts this message.
+
+ ipcIMessageObserver *obs = data->observer;
+ if (!obs)
+ obs = td->observer;
+ NS_ASSERTION(obs, "must at least have a default observer");
+
+ nsresult rv = obs->OnMessageAvailable(status->ClientID(), nsID(), 0, 0);
+ if (rv != IPC_WAIT_NEXT_MESSAGE)
+ data->senderDead = PR_TRUE;
+
+ return IPC_DISCARD_MESSAGE; // consume the message
+ }
+ }
+#ifdef VBOX
+ else if ((data->senderID == IPC_SENDER_ANY ||
+ status->ClientID() == data->senderID) &&
+ status->ClientState() == IPCM_CLIENT_STATE_UP)
+ {
+ LOG(("sender (%d) we're waiting a message from (%d) has come up\n",
+ status->ClientID(), data->senderID));
+ if (data->senderID == IPC_SENDER_ANY)
+ {
+ // inform the observer about the client appearance using a special
+ // null message with an empty target id, but a length of 1.
+
+ ipcIMessageObserver *obs = data->observer;
+ if (!obs)
+ obs = td->observer;
+ NS_ASSERTION(obs, "must at least have a default observer");
+
+ nsresult rv = obs->OnMessageAvailable(status->ClientID(), nsID(), 0, 1);
+ /* VBoxSVC/VBoxXPCOMIPCD auto-start can cause that a client up
+ * message arrives while we're already waiting for a response
+ * from this client. Don't declare the connection as dead in
+ * this case. A client ID wraparound can't falsely trigger
+ * this, since the waiting thread would have hit the liveness
+ * check in the mean time. We MUST consume the message, otherwise
+ * IPCM messages pile up as long as there is a pending call, which
+ * can lead to severe processing overhead. */
+ return IPC_DISCARD_MESSAGE; // consume the message
+ }
+ }
+#endif /* VBOX */
+ break;
+ }
+ default:
+ NS_NOTREACHED("unexpected message");
+ }
+ return IPC_WAIT_NEXT_MESSAGE; // continue iterating
+ }
+
+ nsresult rv = IPC_WAIT_NEXT_MESSAGE;
+
+ if (data->senderID == IPC_SENDER_ANY ||
+ msg->mMetaData == data->senderID)
+ {
+ ipcIMessageObserver *obs = data->observer;
+ if (!obs)
+ obs = td->observer;
+ NS_ASSERTION(obs, "must at least have a default observer");
+
+ rv = obs->OnMessageAvailable(msg->mMetaData,
+ msg->Target(),
+ (const PRUint8 *) msg->Data(),
+ msg->DataLen());
+ }
+
+ // stop iterating if we got a match that the observer accepted.
+ return rv != IPC_WAIT_NEXT_MESSAGE ? NS_OK : IPC_WAIT_NEXT_MESSAGE;
+}
+
+nsresult
+IPC_WaitMessage(PRUint32 aSenderID,
+ const nsID &aTarget,
+ ipcIMessageObserver *aObserver,
+ ipcIMessageObserver *aConsumer,
+ PRIntervalTime aTimeout)
+{
+ NS_ENSURE_TRUE(gClientState, NS_ERROR_NOT_INITIALIZED);
+
+ // do not permit waiting for IPCM messages
+ if (aTarget.Equals(IPCM_TARGET))
+ return NS_ERROR_INVALID_ARG;
+
+ // use aObserver as the message selector
+ WaitMessageSelectorData data = { aSenderID, aObserver, PR_FALSE };
+
+ ipcMessage *msg;
+ nsresult rv = WaitTarget(aTarget, aTimeout, &msg, WaitMessageSelector, &data);
+ if (NS_FAILED(rv))
+ return rv;
+
+ // if the selector has accepted some message, then we pass it to aConsumer
+ // for safe processing. The IPC susbsystem is quite stable here (i.e. we're
+ // not inside any of the monitors, and the message has been already removed
+ // from the pending queue).
+ if (aObserver && aConsumer)
+ {
+ aConsumer->OnMessageAvailable(msg->mMetaData,
+ msg->Target(),
+ (const PRUint8 *) msg->Data(),
+ msg->DataLen());
+ }
+
+ delete msg;
+
+ // if the requested sender has died while waiting, return an error
+ if (data.senderDead)
+ return NS_ERROR_ABORT; // XXX better error code?
+
+ return NS_OK;
+}
+
+/* ------------------------------------------------------------------------- */
+
+nsresult
+IPC_GetID(PRUint32 *aClientID)
+{
+ NS_ENSURE_TRUE(gClientState, NS_ERROR_NOT_INITIALIZED);
+
+ *aClientID = gClientState->selfID;
+ return NS_OK;
+}
+
+nsresult
+IPC_AddName(const char *aName)
+{
+ NS_ENSURE_TRUE(gClientState, NS_ERROR_NOT_INITIALIZED);
+
+ return MakeIPCMRequest(new ipcmMessageClientAddName(aName));
+}
+
+nsresult
+IPC_RemoveName(const char *aName)
+{
+ NS_ENSURE_TRUE(gClientState, NS_ERROR_NOT_INITIALIZED);
+
+ return MakeIPCMRequest(new ipcmMessageClientDelName(aName));
+}
+
+/* ------------------------------------------------------------------------- */
+
+nsresult
+IPC_AddClientObserver(ipcIClientObserver *aObserver)
+{
+ NS_ENSURE_TRUE(gClientState, NS_ERROR_NOT_INITIALIZED);
+
+ return gClientState->clientObservers.AppendObject(aObserver)
+ ? NS_OK : NS_ERROR_OUT_OF_MEMORY;
+}
+
+nsresult
+IPC_RemoveClientObserver(ipcIClientObserver *aObserver)
+{
+ NS_ENSURE_TRUE(gClientState, NS_ERROR_NOT_INITIALIZED);
+
+ for (PRInt32 i = 0; i < gClientState->clientObservers.Count(); ++i)
+ {
+ if (gClientState->clientObservers[i] == aObserver)
+ gClientState->clientObservers.RemoveObjectAt(i);
+ }
+
+ return NS_OK;
+}
+
+/* ------------------------------------------------------------------------- */
+
+// this function could be called on any thread
+nsresult
+IPC_ResolveClientName(const char *aName, PRUint32 *aClientID)
+{
+ NS_ENSURE_TRUE(gClientState, NS_ERROR_NOT_INITIALIZED);
+
+ ipcMessage *msg;
+
+ nsresult rv = MakeIPCMRequest(new ipcmMessageQueryClientByName(aName), &msg);
+ if (NS_FAILED(rv))
+ return rv;
+
+ if (IPCM_GetType(msg) == IPCM_MSG_ACK_CLIENT_ID)
+ *aClientID = ipcMessageCast<ipcmMessageClientID>(msg)->ClientID();
+ else
+ {
+ LOG(("unexpected IPCM response: type=%x\n", IPCM_GetType(msg)));
+ rv = NS_ERROR_UNEXPECTED;
+ }
+
+ delete msg;
+ return rv;
+}
+
+/* ------------------------------------------------------------------------- */
+
+nsresult
+IPC_ClientExists(PRUint32 aClientID, PRBool *aResult)
+{
+ // this is a bit of a hack. we forward a PING to the specified client.
+ // the assumption is that the forwarding will only succeed if the client
+ // exists, so we wait for the RESULT message corresponding to the FORWARD
+ // request. if that gives a successful status, then we know that the
+ // client exists.
+
+ ipcmMessagePing ping;
+
+ return MakeIPCMRequest(new ipcmMessageForward(IPCM_MSG_REQ_FORWARD,
+ aClientID,
+ IPCM_TARGET,
+ ping.Data(),
+ ping.DataLen()));
+}
+
+/* ------------------------------------------------------------------------- */
+
+nsresult
+IPC_SpawnDaemon(const char *path)
+{
+ PRFileDesc *readable = nsnull, *writable = nsnull;
+ PRProcessAttr *attr = nsnull;
+ nsresult rv = NS_ERROR_FAILURE;
+ PRFileDesc *devNull;
+ char *const argv[] = { (char *const) path, nsnull };
+ char c;
+
+ // setup an anonymous pipe that we can use to determine when the daemon
+ // process has started up. the daemon will write a char to the pipe, and
+ // when we read it, we'll know to proceed with trying to connect to the
+ // daemon.
+
+ if (PR_CreatePipe(&readable, &writable) != PR_SUCCESS)
+ goto end;
+ PR_SetFDInheritable(writable, PR_TRUE);
+
+ attr = PR_NewProcessAttr();
+ if (!attr)
+ goto end;
+
+ if (PR_ProcessAttrSetInheritableFD(attr, writable, IPC_STARTUP_PIPE_NAME) != PR_SUCCESS)
+ goto end;
+
+ devNull = PR_Open("/dev/null", PR_RDWR, 0);
+ if (!devNull)
+ goto end;
+
+ PR_ProcessAttrSetStdioRedirect(attr, PR_StandardInput, devNull);
+ PR_ProcessAttrSetStdioRedirect(attr, PR_StandardOutput, devNull);
+ PR_ProcessAttrSetStdioRedirect(attr, PR_StandardError, devNull);
+
+ if (PR_CreateProcessDetached(path, argv, nsnull, attr) != PR_SUCCESS)
+ goto end;
+
+ // Close /dev/null
+ PR_Close(devNull);
+ // close the child end of the pipe in order to get notification on unexpected
+ // child termination instead of being infinitely blocked in PR_Read().
+ PR_Close(writable);
+ writable = nsnull;
+
+ if ((PR_Read(readable, &c, 1) != 1) || (c != IPC_STARTUP_PIPE_MAGIC))
+ goto end;
+
+ rv = NS_OK;
+end:
+ if (readable)
+ PR_Close(readable);
+ if (writable)
+ PR_Close(writable);
+ if (attr)
+ PR_DestroyProcessAttr(attr);
+ return rv;
+}
+
+/* ------------------------------------------------------------------------- */
+
+PR_STATIC_CALLBACK(PLDHashOperator)
+EnumerateTargetMapAndNotify(const nsID &aKey,
+ ipcTargetData *aData,
+ void *aClosure)
+{
+ nsAutoMonitor mon(aData->monitor);
+
+ // wake up anyone waiting on this target.
+ mon.NotifyAll();
+
+ return PL_DHASH_NEXT;
+}
+
+// called on a background thread
+void
+IPC_OnConnectionEnd(nsresult error)
+{
+ // now, go through the target map, and tickle each monitor. that should
+ // unblock any calls to WaitTarget.
+
+#ifndef VBOX_WITH_IPCCLIENT_RW_CS
+ nsAutoMonitor mon(gClientState->monitor);
+#else
+ RTCritSectRwEnterExcl(&gClientState->critSect);
+#endif
+
+ gClientState->connected = PR_FALSE;
+ gClientState->targetMap.EnumerateRead(EnumerateTargetMapAndNotify, nsnull);
+
+#ifdef VBOX_WITH_IPCCLIENT_RW_CS
+ RTCritSectRwLeaveExcl(&gClientState->critSect);
+#endif
+}
+
+/* ------------------------------------------------------------------------- */
+
+static void
+PlaceOnPendingQ(const nsID &target, ipcTargetData *td, ipcMessage *msg)
+{
+ nsAutoMonitor mon(td->monitor);
+
+ // we only want to dispatch a 'ProcessPendingQ' event if we have not
+ // already done so.
+ PRBool dispatchEvent = td->pendingQ.IsEmpty();
+
+ // put this message on our pending queue
+ td->pendingQ.Append(msg);
+
+#ifdef IPC_LOGGING
+ if (IPC_LOG_ENABLED())
+ {
+ char *targetStr = target.ToString();
+ LOG(("placed message on pending queue for target %s and notifying all...\n", targetStr));
+ nsMemory::Free(targetStr);
+ }
+#endif
+
+ // wake up anyone waiting on this queue
+ mon.NotifyAll();
+
+ // proxy call to target's message procedure
+ if (dispatchEvent)
+ CallProcessPendingQ(target, td);
+}
+
+PR_STATIC_CALLBACK(PLDHashOperator)
+EnumerateTargetMapAndPlaceMsg(const nsID &aKey,
+ ipcTargetData *aData,
+ void *userArg)
+{
+ if (!aKey.Equals(IPCM_TARGET))
+ {
+ // place a message clone to a target's event queue
+ ipcMessage *msg = (ipcMessage *) userArg;
+ PlaceOnPendingQ(aKey, aData, msg->Clone());
+ }
+
+ return PL_DHASH_NEXT;
+}
+
+/* ------------------------------------------------------------------------- */
+
+#ifdef IPC_LOGGING
+#include "prprf.h"
+#include <ctype.h>
+#endif
+
+// called on a background thread
+void
+IPC_OnMessageAvailable(ipcMessage *msg)
+{
+#ifdef IPC_LOGGING
+ if (IPC_LOG_ENABLED())
+ {
+ char *targetStr = msg->Target().ToString();
+ LOG(("got message for target: %s\n", targetStr));
+ nsMemory::Free(targetStr);
+
+// IPC_LogBinary((const PRUint8 *) msg->Data(), msg->DataLen());
+ }
+#endif
+
+ if (msg->Target().Equals(IPCM_TARGET))
+ {
+ switch (IPCM_GetType(msg))
+ {
+ // if this is a forwarded message, then post the inner message instead.
+ case IPCM_MSG_PSH_FORWARD:
+ {
+ ipcMessageCast<ipcmMessageForward> fwd(msg);
+ ipcMessage *innerMsg = new ipcMessage(fwd->InnerTarget(),
+ fwd->InnerData(),
+ fwd->InnerDataLen());
+ // store the sender's client id in the meta-data field of the message.
+ innerMsg->mMetaData = fwd->ClientID();
+
+ delete msg;
+
+ // recurse so we can handle forwarded IPCM messages
+ IPC_OnMessageAvailable(innerMsg);
+ return;
+ }
+ case IPCM_MSG_PSH_CLIENT_STATE:
+ {
+ ipcMessageCast<ipcmMessageClientState> status(msg);
+ PostEventToMainThread(new ipcEvent_ClientState(status->ClientID(),
+ status->ClientState()));
+
+ // go through the target map, and place this message to every target's
+ // pending event queue. that unblocks all WaitTarget calls (on all
+ // targets) giving them an opportuninty to finish wait cycle because of
+ // the peer client death, when appropriate.
+#ifndef VBOX_WITH_IPCCLIENT_RW_CS
+ nsAutoMonitor mon(gClientState->monitor);
+#else
+ RTCritSectRwEnterShared(&gClientState->critSect);
+#endif
+
+ gClientState->targetMap.EnumerateRead(EnumerateTargetMapAndPlaceMsg, msg);
+
+#ifdef VBOX_WITH_IPCCLIENT_RW_CS
+ RTCritSectRwLeaveShared(&gClientState->critSect);
+#endif
+ delete msg;
+
+ return;
+ }
+ }
+ }
+
+ nsRefPtr<ipcTargetData> td;
+ if (GetTarget(msg->Target(), getter_AddRefs(td)))
+ {
+ // make copy of target since |msg| may end up pointing to free'd memory
+ // once we notify the monitor inside PlaceOnPendingQ().
+ const nsID target = msg->Target();
+
+ PlaceOnPendingQ(target, td, msg);
+ }
+ else
+ {
+ NS_WARNING("message target is undefined");
+ }
+}