[xpcom] Implement DBus.emitSignal() in the D-Bus XPCOM service
[browser-dbus-bridge.git] / xpcom-dbusservice / DBusService.cpp
1 /**
2  * Browser D-Bus Bridge, XPCOM version
3  *
4  * Copyright © 2008 Movial Creative Technologies Inc
5  *  Contact: Movial Creative Technologies Inc, <info@movial.com>
6  *  Authors: Lauri Mylläri, <lauri.myllari@movial.fi>
7  *           Kalle Vahlman, <kalle.vahlman@movial.com>
8  *
9  * The contents of this file are subject to the Mozilla Public License
10  * Version 1.1 (the "License"); you may not use this file except in
11  * compliance with the License. You may obtain a copy of the License at
12  * http://www.mozilla.org/MPL/
13  *
14  * Software distributed under the License is distributed on an "AS IS"
15  * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
16  * License for the specific language governing rights and limitations
17  * under the License.
18  *
19  * The Original Code is the Browser D-Bus Bridge, XPCOM version.
20  *
21  * The Initial Developer of the Original Code is Movial Creative Technologies
22  * Inc. Portions created by Initial Developer are Copyright (C) 2008
23  * Movial Creative Technologies Inc. All Rights Reserved.
24  *
25  */
26
27 #include <stdio.h>
28 #include <glib.h>
29 #include <dbus/dbus-glib-lowlevel.h>
30 #include <dbus/dbus-glib.h>
31
32
33 #include "nsIGenericFactory.h"
34 #include "nsIInterfaceRequestorUtils.h"
35 #include "nsComponentManagerUtils.h"
36 #include "nsServiceManagerUtils.h"
37
38 #include "nsEmbedString.h"
39 #include "nsIMutableArray.h"
40 #include "nsArrayUtils.h"
41 #include "nsIXPConnect.h"
42
43 #include "IDBusService.h"
44
45 #include "DBusService.h"
46 #include "DBusMethod.h"
47 #include "DBusSignal.h"
48 #include "DBusMarshaling.h"
49
50 #include "bdb-debug.h"
51
52 //
53 // DBusService implementation
54 //
55
56 static
57 DBusHandlerResult _signal_filter(DBusConnection *connection,
58                                  DBusMessage *message,
59                                  void *user_data);
60
61 static DBusService *gDBusService = nsnull;
62
63 NS_IMPL_ISUPPORTS1(DBusService, IDBusService);
64
65 DBusService::DBusService() :
66     mSystemBus(nsnull),
67     mSessionBus(nsnull),
68     mSystemBusHasFilter(PR_FALSE),
69     mSessionBusHasFilter(PR_FALSE)
70 {
71     BDBLOG(("DBusService::DBusService()\n"));
72     mSystemBusSignalObservers.Init();
73     mSessionBusSignalObservers.Init();
74 }
75
76 DBusService::~DBusService()
77 {
78     BDBLOG(("DBusService::~DBusService()\n"));
79     /* FIXME - check if connections need to be released */
80 }
81
82 NS_IMETHODIMP
83 DBusService::GetSignal(PRUint32 aBusType,
84                        const nsACString& aInterfaceName,
85                        const nsACString& aSignalName,
86                        const nsACString& aSender,
87                        const nsACString& aObjectPath,
88                        IDBusSignal **_retval)
89 {
90     nsClassHashtable<nsCStringHashKey, nsTArray<nsWeakPtr> > *signalObservers = nsnull;
91     *_retval = nsnull;
92
93     GetConnection(aBusType);
94
95     if (aBusType == SYSTEM)
96     {
97         if (!mSystemBusHasFilter)
98         {
99             signalObservers = &mSystemBusSignalObservers;
100             mSystemBusHasFilter = PR_TRUE;
101         }
102     }
103     else if (aBusType == SESSION)
104     {
105         if (!mSessionBusHasFilter)
106         {
107             signalObservers = &mSessionBusSignalObservers;
108             mSessionBusHasFilter = PR_TRUE;
109         }
110     }
111     else
112     {
113         BDBLOG(("DBusService::GetSignal(): unknown bus type %d\n", aBusType));
114         return NS_ERROR_ILLEGAL_VALUE;
115     }
116
117     /* add filter only once for each connection */
118     if (signalObservers)
119         dbus_connection_add_filter(GetConnection(aBusType),
120                                    _signal_filter,
121                                    signalObservers,
122                                    nsnull);
123
124     IDBusSignal *signal = new DBusSignal(this,
125                                          aBusType,
126                                          aInterfaceName,
127                                          aSignalName,
128                                          aSender,
129                                          aObjectPath,
130                                          GetCurrentJSContext());
131
132     NS_ENSURE_TRUE(signal, NS_ERROR_OUT_OF_MEMORY);
133
134     NS_ADDREF(*_retval = signal);
135
136     return NS_OK;
137 }
138
139 NS_IMETHODIMP
140 DBusService::GetMethod(PRUint32 aBusType,
141                        const nsACString& aDestination,
142                        const nsACString& aObjectPath,
143                        const nsACString& aMethodName,
144                        const nsACString& aInterfaceName,
145                        const nsACString& aSignature,
146                        IDBusMethod **_retval)
147 {
148     *_retval = nsnull;
149
150     if (!GetConnection(aBusType))
151     {
152         BDBLOG(("DBusService::GetMethod()): invalid bus type %d\n",
153                aBusType));
154         return NS_ERROR_ILLEGAL_VALUE;
155     }
156
157     if (!dbus_signature_validate(PromiseFlatCString(aSignature).get(), nsnull))
158     {
159         BDBLOG(("DBusService::GetMethod()): invalid method signature '%s'\n",
160                PromiseFlatCString(aSignature).get()));
161         return NS_ERROR_ILLEGAL_VALUE;
162     }
163
164     IDBusMethod *method = new DBusMethod(this,
165                                          aBusType,
166                                          aDestination,
167                                          aObjectPath,
168                                          aMethodName,
169                                          aInterfaceName,
170                                          aSignature,
171                                          GetCurrentJSContext());
172
173     NS_ENSURE_TRUE(method, NS_ERROR_OUT_OF_MEMORY);
174
175     NS_ADDREF(*_retval = method);
176
177     return NS_OK;
178 }
179
180 NS_IMETHODIMP DBusService::EmitSignal(PRUint32 busType,
181                                       const nsACString & objectPath,
182                                       const nsACString & interfaceName,
183                                       const nsACString & signalName,
184                                       const nsACString & aSignature,
185                                       nsIVariant **args,
186                                       PRUint32 count,
187                                       PRBool *_retval)
188 {
189     DBusMessage *msg;
190     DBusMessageIter msg_iter;
191     DBusConnection *conn;
192     nsCAutoString signature;
193
194     conn = GetConnection(busType);
195
196     if (!conn)
197     {
198         BDBLOG(("DBusService::EmitSignal()): invalid bus type %d\n",
199                busType));
200         return NS_ERROR_ILLEGAL_VALUE;
201     }
202
203     if (objectPath.IsEmpty()
204      || interfaceName.IsEmpty()
205      || signalName.IsEmpty())
206     {
207         BDBLOG(("DBusService::EmitSignal()): invalid signal arguments\n"));
208         return NS_ERROR_ILLEGAL_VALUE;
209     }
210
211     msg = dbus_message_new_signal(PromiseFlatCString(objectPath).get(),
212                                   PromiseFlatCString(interfaceName).get(),
213                                   PromiseFlatCString(signalName).get());
214     dbus_message_iter_init_append(msg, &msg_iter);
215
216     if (count > 0)
217     {
218         JSContext *cx = GetCurrentJSContext();
219
220         if (aSignature.Equals(""))
221         {
222             for (PRUint32 i = 0; i < count; i++)
223             {
224                 // no method signature specified, guess argument types
225                 nsCOMPtr<nsIVariant> data = args[i];
226                 nsCAutoString tmpsig;
227
228                 getSignatureFromVariant(cx, data, tmpsig);
229                 BDBLOG(("  aArgs[%02d]       : signature \"%s\"\n",
230                        i,
231                        PromiseFlatCString(tmpsig).get()));
232                 signature.Append(tmpsig);
233
234             }
235         } else {
236             signature.Assign(aSignature);
237         }
238
239         if (dbus_signature_validate(PromiseFlatCString(signature).get(), nsnull))
240         {
241             DBusSignatureIter sig_iter;
242             int current_type;
243             int i = 0;
244
245             BDBLOG(("  signature \"%s\"\n", PromiseFlatCString(signature).get()));
246
247             dbus_signature_iter_init(&sig_iter, PromiseFlatCString(signature).get());
248             while ((current_type = dbus_signature_iter_get_current_type(&sig_iter)) != DBUS_TYPE_INVALID)
249             {
250                 char *element_signature = dbus_signature_iter_get_signature(&sig_iter);
251                 BDBLOG(("  element \"%s\" from signature\n", element_signature));
252                 BDBLOG(("  type %c from signature\n", current_type));
253
254                 addVariantToIter(cx, args[i], &msg_iter, &sig_iter);
255
256                 i++;
257                 dbus_free(element_signature);
258                 dbus_signature_iter_next(&sig_iter);
259             }
260         }
261         else
262         {
263             BDBLOG(("  invalid signature \"%s\"\n", PromiseFlatCString(signature).get()));
264             dbus_message_unref(msg);
265             return NS_ERROR_ILLEGAL_VALUE;
266         }
267     }
268
269     if (dbus_connection_send(conn, msg, NULL))
270     {
271         dbus_message_unref(msg);
272         return NS_OK;
273     }
274         
275     dbus_message_unref(msg);
276     return NS_ERROR_UNEXPECTED;
277 }
278  
279
280 DBusPendingCall *DBusService::SendWithReply(PRUint32 aConnType,
281                                             DBusMessage *aMessage,
282                                             PRUint32 aTimeout)
283 {
284     DBusPendingCall *retval = nsnull;
285     DBusConnection *conn = GetConnection(aConnType);
286
287     if (!conn)
288         return nsnull;
289
290     if (!dbus_connection_send_with_reply(conn,
291                                          aMessage,
292                                          &retval,
293                                          aTimeout))
294         return nsnull;
295
296     return retval;
297 }
298
299 DBusMessage *DBusService::SendWithReplyAndBlock(PRUint32 aConnType,
300                                                 DBusMessage *aMessage,
301                                                 PRUint32 aTimeout,
302                                                 DBusError *aError)
303 {
304     DBusConnection *conn = GetConnection(aConnType);
305
306     if (!conn)
307         return nsnull;
308
309     return dbus_connection_send_with_reply_and_block(conn,
310                                                      aMessage,
311                                                      aTimeout,
312                                                      aError);
313 }
314
315 static
316 DBusHandlerResult _signal_filter(DBusConnection *connection,
317                                  DBusMessage *message,
318                                  void *user_data)
319 {
320     if (dbus_message_get_type(message) != DBUS_MESSAGE_TYPE_SIGNAL)
321     {
322         return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
323     }
324
325     nsClassHashtable<nsCStringHashKey, nsTArray<nsWeakPtr> > *observerHash =
326         (nsClassHashtable<nsCStringHashKey, nsTArray<nsWeakPtr> > *) user_data;
327
328     BDBLOG(("_signal_filter: %s.%s\n",
329            dbus_message_get_interface(message),
330            dbus_message_get_member(message)));
331
332     nsCAutoString observerKey;
333
334     observerKey.Assign(dbus_message_get_interface(message));
335     observerKey.Append(NS_LITERAL_CSTRING("."));
336     observerKey.Append(dbus_message_get_member(message));
337
338     BDBLOG(("  observerKey: '%s'\n",
339            PromiseFlatCString(observerKey).get()));
340
341     nsTArray<nsWeakPtr> *observerList = nsnull;
342
343     observerHash->Get(observerKey, &observerList);
344     if (observerList)
345     {
346         BDBLOG(("  got observerList\n"));
347         DBusService *service = DBusService::GetSingleton();
348         
349         for (PRUint32 i = 0; i < observerList->Length(); ++i) {
350             nsCAutoString t;
351             nsCOMPtr<IDBusSignal> signal = do_QueryReferent((*observerList)[i]);
352             signal->GetInterfaceName(t);
353             BDBLOG(("    interface : %s\n", PromiseFlatCString(t).get()));
354             signal->GetSignalName(t);
355             BDBLOG(("    signal    : %s\n", PromiseFlatCString(t).get()));
356             signal->GetSender(t);
357             BDBLOG(("    sender    : %s\n", PromiseFlatCString(t).get()));
358             if (!t.IsEmpty() && !t.Equals(dbus_message_get_sender(message)))
359             {
360                 BDBLOG(("    sender does not match\n"));
361                 break;
362             }
363             signal->GetObjectPath(t);
364             BDBLOG(("    object    : %s\n", PromiseFlatCString(t).get()));
365             if (!t.IsEmpty() && !t.Equals(dbus_message_get_path(message)))
366             {
367                 BDBLOG(("    objectPath does not match\n"));
368                 break;
369             }
370
371             /* do callback */
372
373             DBusMessageIter iter;
374             nsCOMPtr<nsIMutableArray> args_array;
375
376             dbus_message_iter_init(message, &iter);
377             JSContext *cx;
378             signal->GetJSContext(&cx);
379             args_array = getArrayFromIter(cx, &iter);
380
381             PRUint32 arg_items;
382             args_array->GetLength(&arg_items);
383             BDBLOG(("  arg_items: %d items\n", arg_items));
384
385             /* arguments are packed as an array into an nsIVariant */
386             nsIVariant **callback_args = new nsIVariant*[arg_items];
387             nsCOMPtr<nsIWritableVariant> args = do_CreateInstance("@mozilla.org/variant;1");
388             for (PRUint32 i = 0; i < arg_items; i++)
389             {
390                 nsCOMPtr<nsIVariant> arg = do_QueryElementAt(args_array, i);
391                 callback_args[i] = arg;
392                 NS_ADDREF(arg);
393             }
394             args->SetAsArray(nsIDataType::VTYPE_INTERFACE_IS,
395                              &NS_GET_IID(nsIVariant),
396                              arg_items,
397                              callback_args);
398             for (PRUint32 i = 0; i < arg_items; i++)
399                 NS_RELEASE(callback_args[i]);
400             delete[] callback_args;
401
402             nsCOMPtr<IDBusSignalObserver> callback;
403             signal->GetOnEmit(getter_AddRefs(callback));
404
405             service->SetInsideEmit(TRUE);
406             callback->OnSignal(args);
407             service->SetInsideEmit(FALSE);
408
409         }
410         
411         /* Check if we have queued observer changes */
412         service->CheckSignalObserverQueue();
413         
414         return DBUS_HANDLER_RESULT_HANDLED;
415     }
416     else
417     {
418         BDBLOG(("  no observer found\n"));
419         return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
420     }
421 }
422
423 static
424 void BuildRule(IDBusSignal *aSignal, nsACString& aRetval)
425 {
426     nsCAutoString tmp;
427
428     aRetval.Assign(NS_LITERAL_CSTRING("type='signal',interface='"));
429     aSignal->GetInterfaceName(tmp);
430     aRetval.Append(tmp);
431     aRetval.Append(NS_LITERAL_CSTRING("',member='"));
432     aSignal->GetSignalName(tmp);
433     aRetval.Append(tmp);
434     aRetval.Append(NS_LITERAL_CSTRING("'"));
435 }
436
437 void DBusService::CheckSignalObserverQueue()
438 {
439     BDBLOG((__FUNCTION__));
440
441     PRUint32 i = mRemovedSignals.Length();
442     while (i-- > 0)
443     {
444         RemoveSignalObserver(mRemovedSignals[i]);
445         mRemovedSignals.RemoveElementAt(i);
446     }
447     i = mAddedSignals.Length();
448     while (i-- > 0)
449     {
450         AddSignalObserver(mAddedSignals[i]);
451         mAddedSignals.RemoveElementAt(i);
452     }
453 }
454
455 void DBusService::AddSignalObserver(IDBusSignal *aSignal)
456 {
457     nsClassHashtable<nsCStringHashKey, nsTArray<nsWeakPtr> > *signalObservers = nsnull;
458     nsCAutoString observerKey;
459     nsCAutoString tmp;
460
461     if (mInsideEmit)
462     {
463         mAddedSignals.AppendElement(aSignal);
464         return;
465     }
466
467     BDBLOG(("DBusService::AddSignalObserver()\n"));
468
469     aSignal->GetInterfaceName(tmp);
470     BDBLOG(("  aInterface : %s\n", PromiseFlatCString(tmp).get()));
471     observerKey.Assign(tmp);
472     observerKey.Append(".");
473
474     aSignal->GetSignalName(tmp);
475     BDBLOG(("  aSignal    : %s\n", PromiseFlatCString(tmp).get()));
476     observerKey.Append(tmp);
477
478     BDBLOG(("  observerKey: %s\n", PromiseFlatCString(observerKey).get()));
479
480     nsTArray<nsWeakPtr> *observerList = nsnull;
481
482     PRUint32 bus_type = 0;
483     aSignal->GetBusType(&bus_type);
484     if (bus_type == SYSTEM)
485         signalObservers = &mSystemBusSignalObservers;
486     else if (bus_type == SESSION)
487         signalObservers = &mSessionBusSignalObservers;
488     signalObservers->Get(observerKey, &observerList);
489     if (observerList)
490     {
491         /* append to list */
492         BDBLOG(("  got observerList\n"));
493         nsCOMPtr<nsISupportsWeakReference> weakRefable = do_QueryInterface(aSignal);
494         nsWeakPtr weakPtr = getter_AddRefs(NS_GetWeakReference(weakRefable));
495         observerList->AppendElement(weakPtr);
496     }
497     else
498     {
499         /* create a new list */
500         BDBLOG(("  no observerList found\n"));
501         observerList = new nsTArray<nsWeakPtr>;
502         nsCOMPtr<nsISupportsWeakReference> weakRefable = do_QueryInterface(aSignal);
503         nsWeakPtr weakPtr = getter_AddRefs(NS_GetWeakReference(weakRefable));
504         observerList->AppendElement(weakPtr);
505         signalObservers->Put(observerKey, observerList);
506         
507         /* add match rule for interface.signal */
508         PRUint32 busType;
509         nsCAutoString matchRule;
510
511         aSignal->GetBusType(&busType);
512         BuildRule(aSignal, matchRule);
513         BDBLOG(("  new match rule: %s\n", PromiseFlatCString(matchRule).get()));
514         dbus_bus_add_match(GetConnection(busType),
515                            PromiseFlatCString(matchRule).get(),
516                            nsnull);
517     }
518 }
519
520 void DBusService::RemoveSignalObserver(IDBusSignal *aSignal)
521 {
522     nsClassHashtable<nsCStringHashKey, nsTArray<nsWeakPtr> > *signalObservers = nsnull;
523     nsCAutoString observerKey;
524     nsCAutoString tmp;
525
526     if (mInsideEmit)
527     {
528         mRemovedSignals.AppendElement(aSignal);
529         return;
530     }
531
532     BDBLOG(("DBusService::RemoveSignalObserver()\n"));
533
534     aSignal->GetInterfaceName(tmp);
535     BDBLOG(("  aInterface : %s\n", PromiseFlatCString(tmp).get()));
536     observerKey.Assign(tmp);
537     observerKey.Append(".");
538
539     aSignal->GetSignalName(tmp);
540     BDBLOG(("  aSignal    : %s\n", PromiseFlatCString(tmp).get()));
541     observerKey.Append(tmp);
542
543     BDBLOG(("  observerKey: %s\n", PromiseFlatCString(observerKey).get()));
544
545     nsTArray<nsWeakPtr> *observerList = nsnull;
546
547     PRUint32 bus_type = 0;
548     aSignal->GetBusType(&bus_type);
549     if (bus_type == SYSTEM)
550         signalObservers = &mSystemBusSignalObservers;
551     else if (bus_type == SESSION)
552         signalObservers = &mSessionBusSignalObservers;
553     signalObservers->Get(observerKey, &observerList);
554     if (observerList)
555     {
556         BDBLOG(("  got observerList\n"));
557         nsCOMPtr<nsISupportsWeakReference> weakRefable = do_QueryInterface(aSignal);
558         nsWeakPtr weakPtr = getter_AddRefs(NS_GetWeakReference(weakRefable));
559         for (PRUint32 i = 0; i < observerList->Length(); ++i) {
560             nsCAutoString t;
561             nsCAutoString ob;
562             nsCOMPtr<IDBusSignal> signal = do_QueryReferent((*observerList)[i]);
563             signal->GetInterfaceName(t);
564             ob.Assign(t);
565             ob.Append(".");
566             signal->GetSignalName(t);
567             ob.Append(t);
568             BDBLOG(("    signal : %s\n", PromiseFlatCString(ob).get()));
569         }
570         BDBLOG(("  call observerList->RemoveElement\n"));
571         observerList->RemoveElement(weakPtr);
572         for (PRUint32 i = 0; i < observerList->Length(); ++i) {
573             nsCAutoString t;
574             nsCAutoString ob;
575             nsCOMPtr<IDBusSignal> signal = do_QueryReferent((*observerList)[i]);
576             signal->GetInterfaceName(t);
577             ob.Assign(t);
578             ob.Append(".");
579             signal->GetSignalName(t);
580             ob.Append(t);
581             BDBLOG(("    signal : %s\n", PromiseFlatCString(ob).get()));
582         }
583
584         // if list is empty, remove match rule
585         if (observerList->Length() == 0)
586         {
587             PRUint32 busType;
588             nsCAutoString matchRule;
589
590             aSignal->GetBusType(&busType);
591             BuildRule(aSignal, matchRule);
592             BDBLOG(("  remove match rule: %s\n", PromiseFlatCString(matchRule).get()));
593             dbus_bus_remove_match(GetConnection(busType),
594                                   PromiseFlatCString(matchRule).get(),
595                                   nsnull);
596             signalObservers->Remove(observerKey); 
597         }
598         BDBLOG(("  done\n"));
599     }
600     else
601     {
602         BDBLOG(("  ERROR: no observerList found!\n"));
603     }
604 }
605
606 JSContext *DBusService::GetCurrentJSContext()
607 {
608     // try to get a JS context (code borrowed from xpcsample1.cpp)
609
610     // get the xpconnect service
611     nsresult rv;
612     nsCOMPtr<nsIXPConnect> xpc(do_GetService(nsIXPConnect::GetCID(), &rv));
613     if(NS_FAILED(rv))
614         return nsnull;
615     BDBLOG(("    got nsIXPConnect\n"));
616
617     // get the xpconnect native call context
618     nsAXPCNativeCallContext *callContext = nsnull;
619     xpc->GetCurrentNativeCallContext(&callContext);
620     if(!callContext)
621     {
622     BDBLOG(("    callContext :(\n"));
623         return nsnull;
624     }
625     // Get JSContext of current call
626     JSContext* cx;
627     rv = callContext->GetJSContext(&cx);
628     if(NS_FAILED(rv) || !cx)
629         return nsnull;
630     BDBLOG(("    got JSContext\n"));
631
632     return cx;
633 }
634
635 DBusConnection *DBusService::GetConnection(PRUint32 aConnType)
636 {
637     BDBLOG(("DBusService::GetConnection(%d)\n", aConnType));
638
639     if (aConnType == SYSTEM)
640     {
641         if (mSystemBus == nsnull)
642         {
643             mSystemBus = dbus_bus_get(DBUS_BUS_SYSTEM, NULL);
644             if (!mSystemBus)
645                 return nsnull;
646             dbus_connection_set_exit_on_disconnect(mSystemBus, PR_FALSE);
647             dbus_connection_setup_with_g_main(mSystemBus, NULL);
648         }
649         return mSystemBus;
650     }
651     else if (aConnType == SESSION)
652     {
653         if (mSessionBus == nsnull)
654         {
655             mSessionBus = dbus_bus_get(DBUS_BUS_SESSION, NULL);
656             if (!mSessionBus)
657                 return nsnull;
658             dbus_connection_set_exit_on_disconnect(mSessionBus, PR_FALSE);
659             dbus_connection_setup_with_g_main(mSessionBus, NULL);
660         }
661         return mSessionBus;
662     }
663     return nsnull;
664 }
665
666 DBusService *
667 DBusService::GetSingleton()
668 {
669     BDBLOG(("DBusService::GetSingleton() called: "));
670
671     if (!gDBusService)
672     {
673         BDBLOG(("creating new DBusService\n"));
674         gDBusService = new DBusService();
675     }
676
677     if (gDBusService)
678     {
679         BDBLOG(("adding reference to existing DBusService\n"));
680         NS_ADDREF(gDBusService);
681     }
682
683     return gDBusService;
684 }
685
686
687
688 //
689 // Module implementation
690 //
691
692 NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(DBusService, DBusService::GetSingleton);
693
694 static const nsModuleComponentInfo components[] =
695 {
696     {
697         "DBus service",
698         DBUSSERVICE_CID,
699         "@movial.fi/dbus/service;1",
700         DBusServiceConstructor
701     },
702     {
703         "DBus method",
704         DBUSMETHOD_CID,
705         "@movial.fi/dbus/method;1",
706         nsnull
707     },
708     {
709         "DBus signal",
710         DBUSSIGNAL_CID,
711         "@movial.fi/dbus/signal;1",
712         nsnull
713     }
714 };
715
716 NS_IMPL_NSGETMODULE(nsDBusServiceModule, components);
717
718
719
720
721 /* vim: set cindent ts=4 et sw=4: */