Index: configure.in ================================================================================ --- browser/installer/unix/packages-static +++ browser/installer/unix/packages-static @@ -341,6 +341,7 @@ ; bin/components/libnkgnomevfs.so bin/components/libauth.so +bin/components/libdbusservice.so ; [Additional Developer Tools] [adt] --- config/autoconf.mk.in +++ config/autoconf.mk.in @@ -458,6 +458,12 @@ MOZ_GTK2_CFLAGS = @MOZ_GTK2_CFLAGS@ MOZ_GTK2_LIBS = @MOZ_GTK2_LIBS@ +MOZ_DBUS_GLIB_CFLAGS = @MOZ_DBUS_GLIB_CFLAGS@ +MOZ_DBUS_GLIB_LIBS = @MOZ_DBUS_GLIB_LIBS@ + +MOZ_GTHREAD_CFLAGS = @MOZ_GTHREAD_CFLAGS@ +MOZ_GTHREAD_LIBS = @MOZ_GTHREAD_LIBS@ + MOZ_XLIB_CFLAGS = @MOZ_XLIB_CFLAGS@ MOZ_XLIB_LDFLAGS = @MOZ_XLIB_LDFLAGS@ --- configure.in +++ configure.in @@ -4776,6 +4776,33 @@ AC_SUBST(MOZ_GNOMEUI_LIBS) dnl ======================================================== +dnl = dbus support +dnl ======================================================== + +if test "$MOZ_ENABLE_GTK2" +then + MOZ_ARG_ENABLE_BOOL(dbus, + [ --enable-dbus Enable dbus support (default: disabled) ], + MOZ_ENABLE_DBUS=1, + MOZ_ENABLE_DBUS=) + + if test "$MOZ_ENABLE_DBUS" + then + PKG_CHECK_MODULES(MOZ_DBUS_GLIB, dbus-glib-1) + PKG_CHECK_MODULES(MOZ_GTHREAD, gthread-2.0) + fi + + if test "$MOZ_ENABLE_DBUS"; then + AC_DEFINE(MOZ_ENABLE_DBUS) + fi +fi +AC_SUBST(MOZ_ENABLE_DBUS) +AC_SUBST(MOZ_DBUS_GLIB_CFLAGS) +AC_SUBST(MOZ_DBUS_GLIB_LIBS) +AC_SUBST(MOZ_GTHREAD_CFLAGS) +AC_SUBST(MOZ_GTHREAD_LIBS) + +dnl ======================================================== dnl = Setting MOZ_EXTRA_X11CONVERTERS turns on additional dnl = converters in intl/uconv that are used only by X11 gfx dnl = implementations. By default, it's undefined so that @@ -5128,6 +5155,13 @@ MOZ_EXTENSIONS=`echo $MOZ_EXTENSIONS | sed -e 's|gnomevfs||'` fi +if test -z "$MOZ_ENABLE_DBUS" && test `echo "$MOZ_EXTENSIONS" | grep -c dbus` -ne 0; then + if test -n "$MOZ_X11"; then + AC_MSG_WARN([Cannot build dbus without required libraries. Removing dbus from MOZ_EXTENSIONS.]) + fi + MOZ_EXTENSIONS=`echo $MOZ_EXTENSIONS | sed -e 's|dbus||'` +fi + if test -z "$MOZ_JSDEBUGGER" && test `echo "$MOZ_EXTENSIONS" | grep -c venkman` -ne 0; then AC_MSG_WARN([Cannot build venkman without JavaScript debug library. Removing venkman from MOZ_EXTENSIONS.]) MOZ_EXTENSIONS=`echo $MOZ_EXTENSIONS | sed -e 's|venkman||'` --- extensions/dbus/Makefile.in +++ extensions/dbus/Makefile.in @@ -0,0 +1,68 @@ +# ###### BEGIN LICENSE BLOCK ###### +# Version: NPL 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 Novell code. +# +# The Initial Developer of the Original Code is Novell, Inc. +# +# Original Author: Robert O'Callahan (rocallahan@novell.com) +# +# Contributor(s): +# +# 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 NPL, 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 NPL, the GPL or the LGPL. +# +# ###### END LICENSE BLOCK ###### + +DEPTH = ../.. +topsrcdir = @top_srcdir@ +srcdir = @srcdir@ +VPATH = @srcdir@ + +include $(DEPTH)/config/autoconf.mk + +MODULE = dbus +LIBRARY_NAME = dbusservice +GRE_MODULE = 1 +#EXPORT_LIBRARY = 1 +IS_COMPONENT = 1 +FORCE_SHARED_LIB = 1 + +REQUIRES = xpcom \ + string \ + necko \ + embedcomponents \ + $(NULL) + +EXTRA_DSO_LDOPTS = $(MOZ_DBUS_GLIB_LIBS) \ + $(MOZ_GTHREAD_LIBS) \ + $(MOZ_COMPONENT_LIBS) + +CPPSRCS = \ + nsDBusModule.cpp \ + $(NULL) + +include $(topsrcdir)/config/rules.mk + +CXXFLAGS += $(TK_CFLAGS) $(MOZ_DBUS_GLIB_CFLAGS) $(MOZ_GTHREAD_CFLAGS) +CXXFLAGS := $(subst -pedantic,,$(CXXFLAGS)) --- extensions/dbus/nsDBusModule.cpp +++ extensions/dbus/nsDBusModule.cpp @@ -0,0 +1,414 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* vim:expandtab:shiftwidth=4:tabstop=4: + */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: NPL 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 Novell code. + * + * The Initial Developer of the Original Code is Novell, Inc. + * + * Original Author: Robert O'Callahan (rocallahan@novell.com) + * + * Contributor(s): + * + * 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 NPL, 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 NPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#include + +#define DBUS_API_SUBJECT_TO_CHANGE + +#include +#include +#include +#include + +#include "nsIObserverService.h" +#include "nsIObserver.h" +#include "nsIIOService.h" +#include "nsWeakReference.h" +#include "nsIPrefBranch2.h" +#include "nsIPrefService.h" +#include "nsServiceManagerUtils.h" +#include "nsNetCID.h" +#include "nsCRT.h" +#include "nsICategoryManager.h" +#include "nsIGenericFactory.h" +#include "nsIAppStartupNotifier.h" +#include "nsITimer.h" +#include "nsComponentManagerUtils.h" + +#include "nsStringAPI.h" + +// Define NetworkManager API constants. This avoids a dependency on +// NetworkManager-devel. +#define NM_DBUS_SERVICE "org.freedesktop.NetworkManager" +#define NM_DBUS_PATH "/org/freedesktop/NetworkManager" +#define NM_DBUS_INTERFACE "org.freedesktop.NetworkManager" +#define NM_DBUS_SIGNAL_STATE_CHANGE "StateChange" +typedef enum NMState +{ + NM_STATE_UNKNOWN = 0, + NM_STATE_ASLEEP, + NM_STATE_CONNECTING, + NM_STATE_CONNECTED, + NM_STATE_DISCONNECTED +} NMState; + +#define NS_DBUS_CONTRACTID "@mozilla.org/dbus;1" +#define NS_DBUS_CLASSNAME "DBus Interface" +#define NS_DBUS_CID \ + { 0x85899443, \ + 0x2842, \ + 0x4cc4, \ + { 0x87, 0x0c, 0x7a, 0x6f, 0x22, 0xb0, 0xaa, 0x11 } \ + } + +/** + * The nsDBusService component interfaces with DBUS to communicate with daemons + * in systems supporting DBUS. It links dynamically to the DBUS libraries so + * will not load on systems without those libraries ... but that's harmless. + * + * Currently the only daemon we communicate with is NetworkManager. We listen + * for NetworkManager state changes; we set nsIOService's offline status to + * FALSE when NetworkManager reports NM_STATE_CONNECTED, and to TRUE otherwise. + * We also solicit the current status from NetworkManager when this component + * gets loaded. In addition to setting IOService, we also set the + * "browser.offline" preference (because Firefox treats that preference as + * authoritative). We have to wait until prefs have been loaded before setting + * "browser.offline". + * + * In the future we could extend this class to talk to other daemons. + * + * Currently all communication is asynchronous. This isn't hard to implement + * and avoids blocking our main thread. + */ +class nsDBusService : public nsIObserver, + public nsSupportsWeakReference +{ +public: + NS_DECL_ISUPPORTS + NS_DECL_NSIOBSERVER + + nsDBusService(); + virtual ~nsDBusService(); + + nsresult Init(); + + /** + * This gets called when NetworkManager sends us a StateChange signal, + * or when we receive a reply to our inquiry. + * The message contains the current NMState. + */ + void UpdateNetworkStatus(DBusMessage* message); + + /** + * Handle a message. + * @return PR_TRUE to indicate that the message has been handled + */ + PRBool HandleMessage(DBusMessage* message); + + void DoTimerCallback(nsITimer* aTimer); + +private: + /** + * Try to connect to the dbus service. + */ + nsresult ConnectToDBus(); + /** + * Ask NetworkManager to send us the current status. + */ + void RequestNetworkStatus(); + /** + * Update Gecko networking settings based on information we have received. + */ + void SetGeckoNetworkStatus(); + /** + * DBUS has disconnected. Hopefull it will restart; we need to restore + * our connection. + */ + void HandleDBusDisconnect(); + + DBusConnection* mConnection; + nsCOMPtr mReconnectTimer; + + /** + * Set to PR_TRUE after we have received at least one status indication + * from NetworkManager. + */ + PRPackedBool mGotNetworkStatus; + /** + * When mGotNetworkStatus is PR_TRUE, this contains the status indicated + * by NetworkManager. + */ + PRPackedBool mNetworkOnline; + /** + * Set to PR_TRUE after prefs have been loaded. + */ + PRPackedBool mPrefsLoaded; +}; + +NS_IMPL_ISUPPORTS2(nsDBusService, nsIObserver, nsISupportsWeakReference) + +nsDBusService::nsDBusService() { + mConnection = nsnull; + mGotNetworkStatus = PR_FALSE; + mPrefsLoaded = PR_FALSE; +} + +nsDBusService::~nsDBusService() { + if (mConnection) { + dbus_connection_unref(mConnection); + } + if (mReconnectTimer) { + mReconnectTimer->Cancel(); + } +} + +void nsDBusService::SetGeckoNetworkStatus() { + // Don't set Gecko status until after prefs have been loaded. When + // prefs are loaded, we'll get called again. + if (!mPrefsLoaded || !mGotNetworkStatus) + return; + + nsCOMPtr prefs(do_GetService(NS_PREFSERVICE_CONTRACTID)); + if (prefs) { + prefs->SetBoolPref("browser.offline", !mNetworkOnline); + } + + nsCOMPtr ios(do_GetService(NS_IOSERVICE_CONTRACTID)); + if (ios) { + ios->SetOffline(!mNetworkOnline); + } +} + +void nsDBusService::UpdateNetworkStatus(DBusMessage* msg) { + PRInt32 result; + if (dbus_message_get_args(msg, NULL, DBUS_TYPE_UINT32, &result, + DBUS_TYPE_INVALID)) { + mGotNetworkStatus = PR_TRUE; + mNetworkOnline = result == NM_STATE_CONNECTED; + SetGeckoNetworkStatus(); + } +} + +static void NetworkStatusNotify(DBusPendingCall *pending, + void* user_data) { + DBusMessage* msg = dbus_pending_call_steal_reply(pending); + if (!msg) + return; + if (dbus_message_get_type(msg) == DBUS_MESSAGE_TYPE_METHOD_RETURN) { + NS_STATIC_CAST(nsDBusService*, user_data)->UpdateNetworkStatus(msg); + } + dbus_message_unref(msg); +} + +void nsDBusService::RequestNetworkStatus() { + DBusMessage* msg = + dbus_message_new_method_call(NM_DBUS_SERVICE, NM_DBUS_PATH, + NM_DBUS_INTERFACE, "state"); + if (!msg) + return; + + DBusPendingCall* reply; + if (dbus_connection_send_with_reply(mConnection, msg, &reply, -1)) { + dbus_pending_call_set_notify(reply, NetworkStatusNotify, this, NULL); + dbus_pending_call_unref(reply); + } + dbus_message_unref(msg); +} + +void nsDBusService::DoTimerCallback(nsITimer *aTimer) { + if (aTimer == mReconnectTimer.get()) { + nsresult rv = ConnectToDBus(); + if (NS_SUCCEEDED(rv)) { + mReconnectTimer->Cancel(); + mReconnectTimer = nsnull; + } + } +} + +static void TimerCallback(nsITimer *aTimer, void *aClosure) { + NS_STATIC_CAST(nsDBusService*, aClosure)->DoTimerCallback(aTimer); +} + +void nsDBusService::HandleDBusDisconnect() { + if (mConnection) { + dbus_connection_unref(mConnection); + mConnection = nsnull; + } + + nsresult rv; + mReconnectTimer = do_CreateInstance("@mozilla.org/timer;1", &rv); + if (NS_FAILED(rv)) + return; + rv = mReconnectTimer->InitWithFuncCallback(TimerCallback, this, + 5000, nsITimer::TYPE_REPEATING_SLACK); + if (NS_FAILED(rv)) { + mReconnectTimer = nsnull; + return; + } +} + +PRBool nsDBusService::HandleMessage(DBusMessage* message) { + if (dbus_message_is_signal(message, DBUS_INTERFACE_LOCAL, + "Disconnected")) { + HandleDBusDisconnect(); + return PR_FALSE; + } + + if (dbus_message_is_signal(message, NM_DBUS_INTERFACE, + NM_DBUS_SIGNAL_STATE_CHANGE)) { + UpdateNetworkStatus(message); + return PR_TRUE; + } + + return PR_FALSE; +} + +NS_IMETHODIMP nsDBusService::Observe(nsISupports *aSubject, + const char *aTopic, + const PRUnichar *aData) { + if (!aTopic) + return NS_OK; + + if (!nsCRT::strcmp(aTopic, NS_PREFSERVICE_READ_TOPIC_ID)) { + mPrefsLoaded = PR_TRUE; + SetGeckoNetworkStatus(); + } + return NS_OK; +} + +static DBusHandlerResult dbus_filter(DBusConnection* connection, + DBusMessage* message, + void* user_data) { + return NS_STATIC_CAST(nsDBusService*, user_data)->HandleMessage(message) + ? DBUS_HANDLER_RESULT_HANDLED : DBUS_HANDLER_RESULT_NOT_YET_HANDLED; +} + +nsresult nsDBusService::ConnectToDBus() { + DBusError error; + dbus_error_init(&error); + + mConnection = dbus_bus_get(DBUS_BUS_SYSTEM, &error); + if (!mConnection) { + dbus_error_free(&error); + return NS_ERROR_FAILURE; + } + + dbus_connection_set_exit_on_disconnect(mConnection, PR_FALSE); + dbus_connection_setup_with_g_main(mConnection, NULL); + + if (!dbus_connection_add_filter(mConnection, dbus_filter, this, NULL)) { + dbus_error_free(&error); + return NS_ERROR_FAILURE; + } + + dbus_bus_add_match(mConnection, + "type='signal'," + "interface='" NM_DBUS_INTERFACE "'," + "sender='" NM_DBUS_SERVICE "'," + "path='" NM_DBUS_PATH "'", &error); + if (dbus_error_is_set(&error)) { + dbus_error_free(&error); + return NS_ERROR_FAILURE; + } + + RequestNetworkStatus(); + + dbus_error_free(&error); + + return NS_OK; +} + +nsresult nsDBusService::Init() { + nsresult rv; + nsCOMPtr observerService = + do_GetService("@mozilla.org/observer-service;1", &rv); + if (NS_FAILED(rv)) + return rv; + + rv = observerService->AddObserver(this, NS_PREFSERVICE_READ_TOPIC_ID, + PR_FALSE); + if (NS_FAILED(rv)) + return rv; + + if (!g_thread_supported ()) + g_thread_init (NULL); + dbus_g_thread_init(); + + return ConnectToDBus(); +} + +NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsDBusService, Init) + +static NS_METHOD +RegisterDBus(nsIComponentManager *aCompMgr, + nsIFile *aPath, + const char *registryLocation, + const char *componentType, + const nsModuleComponentInfo *info) { + nsresult rv; + + nsCOMPtr + categoryManager(do_GetService(NS_CATEGORYMANAGER_CONTRACTID, &rv)); + if (NS_SUCCEEDED(rv)) { + rv = categoryManager->AddCategoryEntry(APPSTARTUP_CATEGORY, + "DBus Module", + NS_DBUS_CONTRACTID, + PR_TRUE, PR_TRUE, nsnull); + } + + return rv; +} + +static NS_METHOD +UnRegisterDBus(nsIComponentManager *aCompMgr, + nsIFile *aPath, + const char *registryLocation, + const nsModuleComponentInfo *info) { + nsresult rv; + nsCOMPtr + categoryManager(do_GetService(NS_CATEGORYMANAGER_CONTRACTID, &rv)); + if (NS_SUCCEEDED(rv)) { + rv = categoryManager->DeleteCategoryEntry(APPSTARTUP_CATEGORY, + "DBus Module", + PR_TRUE); + } + return rv; +} + +static const nsModuleComponentInfo components[] = { + { NS_DBUS_CLASSNAME, + NS_DBUS_CID, + NS_DBUS_CONTRACTID, + nsDBusServiceConstructor, + RegisterDBus, + UnRegisterDBus, + }, +}; + +NS_IMPL_NSGETMODULE(nsDBusModule, components)