Back to index

lightning-sunbird  0.9+nobinonly
nsGTKRemoteService.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
00002 /* vim:expandtab:shiftwidth=2:tabstop=8:
00003  */
00004 /* ***** BEGIN LICENSE BLOCK *****
00005  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00006  *
00007  * The contents of this file are subject to the Mozilla Public License Version
00008  * 1.1 (the "License"); you may not use this file except in compliance with
00009  * the License. You may obtain a copy of the License at
00010  * http://www.mozilla.org/MPL/
00011  *
00012  * Software distributed under the License is distributed on an "AS IS" basis,
00013  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00014  * for the specific language governing rights and limitations under the
00015  * License.
00016  *
00017  * The Original Code is mozilla.org code.
00018  *
00019  * The Initial Developer of the Original Code is
00020  * Christopher Blizzard.
00021  * Portions created by the Initial Developer are Copyright (C) 2001
00022  * the Initial Developer. All Rights Reserved.
00023  *
00024  * Contributor(s):
00025  *   Christopher Blizzard <blizzard@mozilla.org>
00026  *   Benjamin Smedberg <benjamin@smedbergs.us>
00027  *
00028  * Alternatively, the contents of this file may be used under the terms of
00029  * either the GNU General Public License Version 2 or later (the "GPL"), or
00030  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00031  * in which case the provisions of the GPL or the LGPL are applicable instead
00032  * of those above. If you wish to allow use of your version of this file only
00033  * under the terms of either the GPL or the LGPL, and not to allow others to
00034  * use your version of this file under the terms of the MPL, indicate your
00035  * decision by deleting the provisions above and replace them with the notice
00036  * and other provisions required by the GPL or the LGPL. If you do not delete
00037  * the provisions above, a recipient may use your version of this file under
00038  * the terms of any one of the MPL, the GPL or the LGPL.
00039  *
00040  * ***** END LICENSE BLOCK ***** */
00041 
00042 #include "nsGTKRemoteService.h"
00043 
00044 #include <X11/Xatom.h> // for XA_STRING
00045 #include <stdlib.h>
00046 #include <gtk/gtkinvisible.h> // For some reason GTK+ doesn't include this file
00047                               // automatically from gtk.h
00048 #include <gdk/gdk.h>
00049 #include <gdk/gdkx.h>
00050 
00051 #include "nsIBaseWindow.h"
00052 #include "nsIDocShell.h"
00053 #include "nsIDOMWindow.h"
00054 #include "nsIGenericFactory.h"
00055 #include "nsILocalFile.h"
00056 #include "nsIObserverService.h"
00057 #include "nsIScriptGlobalObject.h"
00058 #include "nsIServiceManager.h"
00059 #include "nsIWeakReference.h"
00060 #include "nsIWidget.h"
00061 
00062 #include "nsCOMPtr.h"
00063 #include "nsString.h"
00064 #include "prprf.h"
00065 #include "prenv.h"
00066 #include "nsCRT.h"
00067 
00068 #ifdef MOZ_XUL_APP
00069 #include "nsICommandLineRunner.h"
00070 #include "nsXULAppAPI.h"
00071 #else
00072 #include "nsISuiteRemoteService.h"
00073 #endif
00074 
00075 #define MOZILLA_VERSION_PROP   "_MOZILLA_VERSION"
00076 #define MOZILLA_LOCK_PROP      "_MOZILLA_LOCK"
00077 #define MOZILLA_COMMAND_PROP   "_MOZILLA_COMMAND"
00078 #define MOZILLA_RESPONSE_PROP  "_MOZILLA_RESPONSE"
00079 #define MOZILLA_USER_PROP      "_MOZILLA_USER"
00080 #define MOZILLA_PROFILE_PROP   "_MOZILLA_PROFILE"
00081 #define MOZILLA_PROGRAM_PROP   "_MOZILLA_PROGRAM"
00082 #define MOZILLA_COMMANDLINE_PROP "_MOZILLA_COMMANDLINE"
00083 
00084 #ifdef IS_BIG_ENDIAN
00085 #define TO_LITTLE_ENDIAN32(x) \
00086     ((((x) & 0xff000000) >> 24) | (((x) & 0x00ff0000) >> 8) | \
00087     (((x) & 0x0000ff00) << 8) | (((x) & 0x000000ff) << 24))
00088 #else
00089 #define TO_LITTLE_ENDIAN32(x) (x)
00090 #endif
00091 
00092 #ifdef MOZ_XUL_APP
00093 const unsigned char kRemoteVersion[] = "5.1";
00094 #else
00095 const unsigned char kRemoteVersion[] = "5.0";
00096 #endif
00097 
00098 NS_IMPL_QUERY_INTERFACE2(nsGTKRemoteService,
00099                          nsIRemoteService,
00100                          nsIObserver)
00101 
00102 NS_IMETHODIMP_(nsrefcnt)
00103 nsGTKRemoteService::AddRef()
00104 {
00105   return 1;
00106 }
00107 
00108 NS_IMETHODIMP_(nsrefcnt)
00109 nsGTKRemoteService::Release()
00110 {
00111   return 1;
00112 }
00113 
00114 NS_IMETHODIMP
00115 nsGTKRemoteService::Startup(const char* aAppName, const char* aProfileName)
00116 {
00117   NS_ASSERTION(aAppName, "Don't pass a null appname!");
00118 
00119   EnsureAtoms();
00120   if (mServerWindow) return NS_ERROR_ALREADY_INITIALIZED;
00121 
00122   mAppName = aAppName;
00123   ToLowerCase(mAppName);
00124 
00125   mProfileName = aProfileName;
00126 
00127   mServerWindow = gtk_invisible_new();
00128   gtk_widget_realize(mServerWindow);
00129   HandleCommandsFor(mServerWindow, nsnull);
00130 
00131   if (!mWindows.IsInitialized())
00132     mWindows.Init();
00133 
00134   mWindows.EnumerateRead(StartupHandler, this);
00135 
00136   nsCOMPtr<nsIObserverService> obs
00137     (do_GetService("@mozilla.org/observer-service;1"));
00138   if (obs) {
00139     obs->AddObserver(this, "xpcom-shutdown", PR_FALSE);
00140     obs->AddObserver(this, "quit-application", PR_FALSE);
00141   }
00142 
00143   return NS_OK;
00144 }
00145 
00146 PLDHashOperator
00147 nsGTKRemoteService::StartupHandler(const void* aKey,
00148                                    nsIWeakReference* aData,
00149                                    void* aClosure)
00150 {
00151   GtkWidget* widget = (GtkWidget*) aKey;
00152   nsGTKRemoteService* aThis = (nsGTKRemoteService*) aClosure;
00153 
00154   aThis->HandleCommandsFor(widget, aData);
00155   return PL_DHASH_NEXT;
00156 }
00157 
00158 NS_IMETHODIMP
00159 nsGTKRemoteService::RegisterWindow(nsIDOMWindow* aWindow)
00160 {
00161   // get the native window for this instance
00162   nsCOMPtr<nsIScriptGlobalObject> scriptObject
00163     (do_QueryInterface(aWindow));
00164   NS_ENSURE_TRUE(scriptObject, NS_ERROR_FAILURE);
00165 
00166   nsCOMPtr<nsIBaseWindow> baseWindow
00167     (do_QueryInterface(scriptObject->GetDocShell()));
00168   NS_ENSURE_TRUE(baseWindow, NS_ERROR_FAILURE);
00169 
00170   nsCOMPtr<nsIWidget> mainWidget;
00171   baseWindow->GetMainWidget(getter_AddRefs(mainWidget));
00172   NS_ENSURE_TRUE(mainWidget, NS_ERROR_FAILURE);
00173 
00174   // walk up the widget tree and find the toplevel window in the
00175   // hierarchy
00176 
00177   nsCOMPtr<nsIWidget> tempWidget (dont_AddRef(mainWidget->GetParent()));
00178 
00179   while (tempWidget) {
00180     tempWidget = dont_AddRef(tempWidget->GetParent());
00181     if (tempWidget)
00182       mainWidget = tempWidget;
00183   }
00184 
00185   GtkWidget* widget =
00186     (GtkWidget*) mainWidget->GetNativeData(NS_NATIVE_SHELLWIDGET);
00187   NS_ENSURE_TRUE(widget, NS_ERROR_FAILURE);
00188 
00189   nsCOMPtr<nsIWeakReference> weak = do_GetWeakReference(aWindow);
00190   NS_ENSURE_TRUE(weak, NS_ERROR_FAILURE);
00191 
00192   if (!mWindows.IsInitialized())
00193     mWindows.Init();
00194 
00195   mWindows.Put(widget, weak);
00196 
00197   // If Startup() has already been called, immediately register this window.
00198   if (mServerWindow) {
00199     HandleCommandsFor(widget, weak);
00200   }
00201 
00202   return NS_OK;
00203 }
00204 
00205 NS_IMETHODIMP
00206 nsGTKRemoteService::Shutdown()
00207 {
00208   if (!mServerWindow)
00209     return NS_ERROR_NOT_INITIALIZED;
00210 
00211   gtk_widget_destroy(mServerWindow);
00212   mServerWindow = nsnull;
00213   return NS_OK;
00214 }
00215 
00216 NS_IMETHODIMP
00217 nsGTKRemoteService::Observe(nsISupports* aSubject,
00218                             const char *aTopic,
00219                             const PRUnichar *aData)
00220 {
00221   // This can be xpcom-shutdown or quit-application, but it's the same either
00222   // way.
00223   Shutdown();
00224   return NS_OK;
00225 }
00226 
00227 #define ARRAY_LENGTH(array_) (sizeof(array_)/sizeof(array_[0]))
00228 
00229 // Minimize the roundtrips to the X server by getting all the atoms at once
00230 static char *XAtomNames[] = {
00231   MOZILLA_VERSION_PROP,
00232   MOZILLA_LOCK_PROP,
00233   MOZILLA_COMMAND_PROP,
00234   MOZILLA_RESPONSE_PROP,
00235   MOZILLA_USER_PROP,
00236   MOZILLA_PROFILE_PROP,
00237   MOZILLA_PROGRAM_PROP,
00238   MOZILLA_COMMANDLINE_PROP
00239 };
00240 static Atom XAtoms[ARRAY_LENGTH(XAtomNames)];
00241 
00242 void
00243 nsGTKRemoteService::EnsureAtoms(void)
00244 {
00245   if (sMozVersionAtom)
00246     return;
00247 
00248   XInternAtoms(GDK_DISPLAY(), XAtomNames, ARRAY_LENGTH(XAtomNames),
00249                False, XAtoms);
00250   int i = 0;
00251   sMozVersionAtom     = XAtoms[i++];
00252   sMozLockAtom        = XAtoms[i++];
00253   sMozCommandAtom     = XAtoms[i++];
00254   sMozResponseAtom    = XAtoms[i++];
00255   sMozUserAtom        = XAtoms[i++];
00256   sMozProfileAtom     = XAtoms[i++];
00257   sMozProgramAtom     = XAtoms[i++];
00258   sMozCommandLineAtom = XAtoms[i++];
00259 }
00260 
00261 #ifndef MOZ_XUL_APP
00262 const char*
00263 nsGTKRemoteService::HandleCommand(char* aCommand, nsIDOMWindow* aWindow)
00264 {
00265   nsresult rv;
00266 
00267   nsCOMPtr<nsISuiteRemoteService> remote
00268     (do_GetService("@mozilla.org/browser/xremoteservice;2"));
00269   if (!remote)
00270     return "509 internal error";
00271 
00272   rv = remote->ParseCommand(aCommand, aWindow);
00273   if (NS_SUCCEEDED(rv))
00274     return "200 executed command";
00275 
00276   if (NS_ERROR_INVALID_ARG == rv)
00277     return "500 command not parseable";
00278 
00279   if (NS_ERROR_NOT_IMPLEMENTED == rv)
00280     return "501 unrecognized command";
00281 
00282   return "509 internal error";
00283 }
00284 
00285 #else //MOZ_XUL_APP
00286 const char*
00287 nsGTKRemoteService::HandleCommand(char* aCommand, nsIDOMWindow* aWindow)
00288 {
00289   nsresult rv;
00290 
00291   nsCOMPtr<nsICommandLineRunner> cmdline
00292     (do_CreateInstance("@mozilla.org/toolkit/command-line;1", &rv));
00293   if (NS_FAILED(rv))
00294     return "509 internal error";
00295 
00296   // 1) Make sure that it looks remotely valid with parens
00297   // 2) Treat ping() immediately and specially
00298 
00299   nsCAutoString command(aCommand);
00300   PRInt32 p1, p2;
00301   p1 = command.FindChar('(');
00302   p2 = command.FindChar(')');
00303 
00304   if (p1 == kNotFound || p2 == kNotFound || p1 == 0 || p2 < p1) {
00305     return "500 command not parseable";
00306   }
00307 
00308   command.Truncate(p1);
00309   command.Trim(" ", PR_TRUE, PR_TRUE);
00310   ToLowerCase(command);
00311 
00312 #ifdef DEBUG_bsmedberg
00313   printf("Processing xremote command: %s\n", command.get());
00314 #endif
00315 
00316   if (!command.EqualsLiteral("ping")) {
00317     char* argv[3] = {"dummyappname", "-remote", aCommand};
00318     rv = cmdline->Init(3, argv, nsnull, nsICommandLine::STATE_REMOTE_EXPLICIT);
00319     if (NS_FAILED(rv))
00320       return "509 internal error";
00321 
00322     if (aWindow)
00323       cmdline->SetWindowContext(aWindow);
00324 
00325     rv = cmdline->Run();
00326     if (NS_ERROR_ABORT == rv)
00327       return "500 command not parseable";
00328     if (NS_FAILED(rv))
00329       return "509 internal error";
00330   }
00331 
00332   return "200 executed command";
00333 }
00334 
00335 const char*
00336 nsGTKRemoteService::HandleCommandLine(char* aBuffer, nsIDOMWindow* aWindow)
00337 {
00338   nsresult rv;
00339 
00340   nsCOMPtr<nsICommandLineRunner> cmdline
00341     (do_CreateInstance("@mozilla.org/toolkit/command-line;1", &rv));
00342   if (NS_FAILED(rv))
00343     return "509 internal error";
00344 
00345   // the commandline property is constructed as an array of PRInt32
00346   // followed by a series of null-terminated strings:
00347   //
00348   // [argc][offsetargv0][offsetargv1...]<workingdir>\0<argv[0]>\0argv[1]...\0
00349   // (offset is from the beginning of the buffer)
00350 
00351   PRInt32 argc = TO_LITTLE_ENDIAN32(*NS_REINTERPRET_CAST(PRInt32*, aBuffer));
00352   char *wd   = aBuffer + ((argc + 1) * sizeof(PRInt32));
00353 
00354 #ifdef DEBUG_bsmedberg
00355   printf("Receiving command line:\n"
00356          "  wd:\t%s\n"
00357          "  argc:\t%i\n",
00358          wd, argc);
00359 #endif
00360 
00361   nsCOMPtr<nsILocalFile> lf;
00362   rv = NS_NewNativeLocalFile(nsDependentCString(wd), PR_TRUE,
00363                              getter_AddRefs(lf));
00364   if (NS_FAILED(rv))
00365     return "509 internal error";
00366 
00367   char **argv = (char**) malloc(sizeof(char*) * argc);
00368   if (!argv) return "509 internal error";
00369 
00370   PRInt32  *offset = NS_REINTERPRET_CAST(PRInt32*, aBuffer) + 1;
00371 
00372   for (int i = 0; i < argc; ++i) {
00373     argv[i] = aBuffer + TO_LITTLE_ENDIAN32(offset[i]);
00374 
00375 #ifdef DEBUG_bsmedberg
00376     printf("  argv[%i]:\t%s\n", i, argv[i]);
00377 #endif
00378   }
00379 
00380   rv = cmdline->Init(argc, argv, lf, nsICommandLine::STATE_REMOTE_AUTO);
00381   free (argv);
00382   if (NS_FAILED(rv)) {
00383     return "509 internal error";
00384   }
00385 
00386   if (aWindow)
00387     cmdline->SetWindowContext(aWindow);
00388 
00389   rv = cmdline->Run();
00390   if (NS_ERROR_ABORT == rv)
00391     return "500 command not parseable";
00392   
00393   if (NS_FAILED(rv))
00394     return "509 internal error";
00395 
00396   return "200 executed command";
00397 }
00398 #endif // MOZ_XUL_APP
00399 
00400 void
00401 nsGTKRemoteService::HandleCommandsFor(GtkWidget* widget,
00402                                       nsIWeakReference* aWindow)
00403 {
00404 #ifdef MOZ_WIDGET_GTK2
00405   g_signal_connect(G_OBJECT(widget), "property_notify_event",
00406                    G_CALLBACK(HandlePropertyChange), aWindow);
00407 #else // GTK+
00408   gtk_signal_connect(GTK_OBJECT(widget), "property_notify_event",
00409                      GTK_SIGNAL_FUNC(HandlePropertyChange), aWindow);
00410 #endif
00411 
00412   gtk_widget_add_events(widget, GDK_PROPERTY_CHANGE_MASK);
00413 
00414   Window window = GDK_WINDOW_XWINDOW(widget->window);
00415 
00416   // set our version
00417   XChangeProperty(GDK_DISPLAY(), window, sMozVersionAtom, XA_STRING,
00418                   8, PropModeReplace, kRemoteVersion, sizeof(kRemoteVersion) - 1);
00419 
00420   // get our username
00421   unsigned char *logname;
00422   logname = (unsigned char*) PR_GetEnv("LOGNAME");
00423   if (logname) {
00424     // set the property on the window if it's available
00425     XChangeProperty(GDK_DISPLAY(), window, sMozUserAtom, XA_STRING,
00426                     8, PropModeReplace, logname, strlen((char*) logname));
00427   }
00428 
00429   XChangeProperty(GDK_DISPLAY(), window, sMozProgramAtom, XA_STRING,
00430                   8, PropModeReplace, (unsigned char*) mAppName.get(), mAppName.Length());
00431 
00432   if (!mProfileName.IsEmpty()) {
00433     XChangeProperty(GDK_DISPLAY(), window, sMozProfileAtom, XA_STRING,
00434                     8, PropModeReplace, (unsigned char*) mProfileName.get(), mProfileName.Length());
00435   }
00436 }
00437 
00438 #ifdef MOZ_WIDGET_GTK2
00439 #define CMP_GATOM_XATOM(gatom,xatom) (gatom == gdk_x11_xatom_to_atom(xatom))
00440 #else
00441 #define CMP_GATOM_XATOM(gatom,xatom) (gatom == xatom)
00442 #endif
00443 
00444 gboolean
00445 nsGTKRemoteService::HandlePropertyChange(GtkWidget *aWidget,
00446                                          GdkEventProperty *pevent,
00447                                          nsIWeakReference* aThis)
00448 {
00449   nsCOMPtr<nsIDOMWindow> window (do_QueryReferent(aThis));
00450 
00451   if (pevent->state == GDK_PROPERTY_NEW_VALUE &&
00452       CMP_GATOM_XATOM(pevent->atom, sMozCommandAtom)) {
00453 
00454     // We got a new command atom.
00455     int result;
00456     Atom actual_type;
00457     int actual_format;
00458     unsigned long nitems, bytes_after;
00459     char *data = 0;
00460 
00461     result = XGetWindowProperty (GDK_DISPLAY(),
00462                                  GDK_WINDOW_XWINDOW(pevent->window),
00463                                  sMozCommandAtom,
00464                                  0,                        /* long_offset */
00465                                  (65536 / sizeof (long)),  /* long_length */
00466                                  True,                     /* atomic delete after */
00467                                  XA_STRING,                /* req_type */
00468                                  &actual_type,             /* actual_type return */
00469                                  &actual_format,           /* actual_format_return */
00470                                  &nitems,                  /* nitems_return */
00471                                  &bytes_after,             /* bytes_after_return */
00472                                  (unsigned char **)&data); /* prop_return
00473                                                               (we only care
00474                                                               about the first ) */
00475 
00476 #ifdef DEBUG_bsmedberg
00477     printf("Handling command: %s\n", data);
00478 #endif
00479 
00480     // Failed to get property off the window?
00481     if (result != Success)
00482       return FALSE;
00483 
00484     // Failed to get the data off the window or it was the wrong type?
00485     if (!data || !TO_LITTLE_ENDIAN32(*NS_REINTERPRET_CAST(PRInt32*, data)))
00486       return FALSE;
00487 
00488     // cool, we got the property data.
00489     const char *response = HandleCommand(data, window);
00490 
00491     // put the property onto the window as the response
00492     XChangeProperty (GDK_DISPLAY(), GDK_WINDOW_XWINDOW(pevent->window),
00493                      sMozResponseAtom, XA_STRING,
00494                      8, PropModeReplace, (const unsigned char *)response, strlen (response));
00495     XFree(data);
00496     return TRUE;
00497   }
00498 
00499 #ifdef MOZ_XUL_APP
00500   if (pevent->state == GDK_PROPERTY_NEW_VALUE &&
00501       CMP_GATOM_XATOM(pevent->atom, sMozCommandLineAtom)) {
00502 
00503     // We got a new commandline atom.
00504     int result;
00505     Atom actual_type;
00506     int actual_format;
00507     unsigned long nitems, bytes_after;
00508     char *data = 0;
00509 
00510     result = XGetWindowProperty (GDK_DISPLAY(),
00511                                  GDK_WINDOW_XWINDOW(pevent->window),
00512                                  sMozCommandLineAtom,
00513                                  0,                        /* long_offset */
00514                                  (65536 / sizeof (long)),  /* long_length */
00515                                  True,                     /* atomic delete after */
00516                                  XA_STRING,                /* req_type */
00517                                  &actual_type,             /* actual_type return */
00518                                  &actual_format,           /* actual_format_return */
00519                                  &nitems,                  /* nitems_return */
00520                                  &bytes_after,             /* bytes_after_return */
00521                                  (unsigned char **)&data); /* prop_return
00522                                                               (we only care
00523                                                               about the first ) */
00524 
00525     // Failed to get property off the window?
00526     if (result != Success)
00527       return FALSE;
00528 
00529     // Failed to get the data off the window or it was the wrong type?
00530     if (!data || !TO_LITTLE_ENDIAN32(*NS_REINTERPRET_CAST(PRInt32*, data)))
00531       return FALSE;
00532 
00533     // cool, we got the property data.
00534     const char *response = HandleCommandLine(data, window);
00535 
00536     // put the property onto the window as the response
00537     XChangeProperty (GDK_DISPLAY(), GDK_WINDOW_XWINDOW(pevent->window),
00538                      sMozResponseAtom, XA_STRING,
00539                      8, PropModeReplace, (const unsigned char *)response, strlen (response));
00540     XFree(data);
00541     return TRUE;
00542   }
00543 #endif //MOZ_XUL_APP
00544 
00545   if (pevent->state == GDK_PROPERTY_NEW_VALUE && 
00546       CMP_GATOM_XATOM(pevent->atom, sMozResponseAtom)) {
00547     // client accepted the response.  party on wayne.
00548     return TRUE;
00549   }
00550 
00551   if (pevent->state == GDK_PROPERTY_NEW_VALUE && 
00552       CMP_GATOM_XATOM(pevent->atom, sMozLockAtom)) {
00553     // someone locked the window
00554     return TRUE;
00555   }
00556 
00557   return FALSE;
00558 }
00559 
00560 Atom nsGTKRemoteService::sMozVersionAtom;
00561 Atom nsGTKRemoteService::sMozLockAtom;
00562 Atom nsGTKRemoteService::sMozCommandAtom;
00563 Atom nsGTKRemoteService::sMozResponseAtom;
00564 Atom nsGTKRemoteService::sMozUserAtom;
00565 Atom nsGTKRemoteService::sMozProfileAtom;
00566 Atom nsGTKRemoteService::sMozProgramAtom;
00567 Atom nsGTKRemoteService::sMozCommandLineAtom;
00568 
00569 // {C0773E90-5799-4eff-AD03-3EBCD85624AC}
00570 #define NS_REMOTESERVICE_CID \
00571   { 0xc0773e90, 0x5799, 0x4eff, { 0xad, 0x3, 0x3e, 0xbc, 0xd8, 0x56, 0x24, 0xac } }
00572 
00573 NS_GENERIC_FACTORY_CONSTRUCTOR(nsGTKRemoteService)
00574 
00575 static const nsModuleComponentInfo components[] =
00576 {
00577   { "Remote Service",
00578     NS_REMOTESERVICE_CID,
00579     "@mozilla.org/toolkit/remote-service;1",
00580     nsGTKRemoteServiceConstructor
00581   }
00582 };
00583 
00584 NS_IMPL_NSGETMODULE(RemoteServiceModule, components)