Import the XPCOM version
[browser-dbus-bridge.git] / xpcom-dbusservice / DBusMarshaling.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
29 #include "nsEmbedString.h"
30 #include "nsComponentManagerUtils.h"
31 #include "nsServiceManagerUtils.h"
32 #include "nsArrayUtils.h"
33 #include "nsMemory.h"
34 #include "nsISupportsPrimitives.h"
35 #include "nsIProperties.h"
36 #include "nsIXPConnect.h"
37
38 #include "DBusMarshaling.h"
39
40 #include "bdb-debug.h"
41
42 void addArrayDataToIter(void *data_ptr, PRUint32 count, PRUint16 type, DBusMessageIter *aIter, DBusSignatureIter *aSigIter);
43 void addJSValueToIter(JSContext *cx, jsval *aValue, DBusMessageIter *aIter, DBusSignatureIter *aSigIter);
44
45 static void
46 listJSObjectProperties(JSContext *cx, JSObject *js_obj)
47 {
48     JSIdArray *props = JS_Enumerate(cx, js_obj);
49     if (props)
50     {
51         BDBLOG(("  listJSObjectProperties: got JSIdArray with %i props\n", props->length));
52
53         for (int i = 0; i < props->length; i++)
54         {
55             jsval v;
56             const PRUnichar *name;
57             JS_IdToValue( cx, props->vector[i], &v );
58             JSString *prop_string = JS_ValueToString(cx, v);
59             name = reinterpret_cast<const PRUnichar*>(JS_GetStringChars(prop_string));
60             nsDependentString name_s(name);
61             const char *utf8prop = NS_ConvertUTF16toUTF8(name_s).get();
62             BDBLOG(("    property %s\n", utf8prop));
63
64             jsval value;
65             if (JS_LookupUCProperty(cx,
66                                     js_obj,
67                                     JS_GetStringChars(prop_string),
68                                     JS_GetStringLength(prop_string),
69                                     &value) == JS_TRUE)
70             {
71                 JSString *val_string = JS_ValueToString(cx, value);
72                 const PRUnichar *valstr = reinterpret_cast<const PRUnichar*>(JS_GetStringChars(val_string));
73                 nsDependentString val_s(valstr);
74                 const char *utf8val = NS_ConvertUTF16toUTF8(val_s).get();
75                 BDBLOG(("    value %s\n", utf8val));
76             }
77         }
78         JS_DestroyIdArray(cx, props);
79     }
80 }
81
82 void
83 getSignatureFromJSValue(JSContext *cx, jsval *aValue, nsCString &aResult)
84 {
85     aResult.Assign(DBUS_TYPE_INVALID_AS_STRING);
86
87     if (JSVAL_IS_BOOLEAN(*aValue))
88         aResult.Assign(DBUS_TYPE_BOOLEAN_AS_STRING);
89     else if (JSVAL_IS_INT(*aValue))
90         aResult.Assign(DBUS_TYPE_INT32_AS_STRING);
91     else if (JSVAL_IS_DOUBLE(*aValue))
92         aResult.Assign(DBUS_TYPE_DOUBLE_AS_STRING);
93     else if (JSVAL_IS_STRING(*aValue))
94         aResult.Assign(DBUS_TYPE_STRING_AS_STRING);
95     else if (JSVAL_IS_OBJECT(*aValue) && JS_IsArrayObject(cx, JSVAL_TO_OBJECT(*aValue)))
96     {
97         // guess element type from first property value
98
99         JSIdArray *props = JS_Enumerate(cx, JSVAL_TO_OBJECT(*aValue));
100         if (props)
101         {
102             BDBLOG(("    got JSIdArray\n"));
103             aResult.Assign(DBUS_TYPE_ARRAY_AS_STRING);
104
105             // get key signature from first property name
106             jsval propname;
107             nsCAutoString tmpsig;
108             JS_IdToValue(cx, props->vector[0], &propname);
109
110             jsval propvalue;
111             JSString *prop_string = JS_ValueToString(cx, propname);
112             if (JS_LookupUCProperty(cx,
113                                     JSVAL_TO_OBJECT(*aValue),
114                                     JS_GetStringChars(prop_string),
115                                     JS_GetStringLength(prop_string),
116                                     &propvalue) == JS_TRUE)
117             {
118                 getSignatureFromJSValue(cx, &propvalue, tmpsig);
119                 aResult.Append(tmpsig);
120             }
121             else
122             {
123                 // FIXME - could not find property value??
124                 // assume string to keep signature valid
125                 aResult.Append(DBUS_TYPE_STRING_AS_STRING);
126             }
127             JS_DestroyIdArray(cx, props);
128         }
129
130     }
131     else if (JSVAL_IS_OBJECT(*aValue))
132     {
133         nsISupports* supports;
134         JSClass* clazz;
135         JSObject* parent;
136         JSObject* glob = JSVAL_TO_OBJECT(*aValue);
137
138         clazz = JS_GET_CLASS(cx, JS_GetParent(cx, glob));
139
140         if (!clazz ||
141             !(clazz->flags & JSCLASS_HAS_PRIVATE) ||
142             !(clazz->flags & JSCLASS_PRIVATE_IS_NSISUPPORTS) ||
143             !(supports = (nsISupports*) JS_GetPrivate(cx, glob))) {
144
145             BDBLOG(("  getSignatureFromJSValue: could not find nsISupports inside object, assume dictionary\n"));
146
147             // try to enumerate object properties
148             JSIdArray *props = JS_Enumerate(cx, glob);
149             if (props)
150             {
151                 BDBLOG(("    got JSIdArray with %i props\n", props->length));
152                 aResult.Assign(DBUS_TYPE_ARRAY_AS_STRING);
153                 aResult.Append(DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING);
154
155                 // get key signature from first property name
156                 jsval propname;
157                 nsCAutoString tmpsig;
158                 JS_IdToValue(cx, props->vector[0], &propname);
159                 getSignatureFromJSValue(cx, &propname, tmpsig);
160                 aResult.Append(tmpsig);
161
162                 jsval propvalue;
163                 JSString *prop_string = JS_ValueToString(cx, propname);
164                 if (JS_LookupUCProperty(cx,
165                                         glob,
166                                         JS_GetStringChars(prop_string),
167                                         JS_GetStringLength(prop_string),
168                                         &propvalue) == JS_TRUE)
169                 {
170                     getSignatureFromJSValue(cx, &propvalue, tmpsig);
171                     aResult.Append(tmpsig);
172                 }
173                 else
174                 {
175                     // FIXME - could not find property value??
176                     // assume string to keep signature valid
177                     aResult.Append(DBUS_TYPE_STRING_AS_STRING);
178                 }
179                 aResult.Append(DBUS_DICT_ENTRY_END_CHAR_AS_STRING);
180                 JS_DestroyIdArray(cx, props);
181             }
182
183         }
184         else
185         {
186             BDBLOG(("  getSignatureFromJSValue: clazz->name %s\n", clazz->name));
187             // test argument for nsIXPConnectWrappedNative
188             nsCOMPtr<nsIXPConnectWrappedNative> wrappednative = do_QueryInterface(supports);
189             if (wrappednative)
190             {
191                 BDBLOG(("  getSignatureFromJSValue: got nsIXPConnectWrappedNative\n"));
192                 nsCOMPtr<nsIVariant> variant = do_QueryInterface(wrappednative->Native());
193                 if (variant)
194                 {
195                     BDBLOG(("    found wrapped variant\n"));
196                     getSignatureFromVariant(variant, aResult);
197                     return;
198                 }
199             }
200             // use string type as fallback
201             aResult.Assign(DBUS_TYPE_STRING_AS_STRING);
202             return;
203         }
204
205     }
206 }
207
208 void
209 getSignatureFromVariantType(PRUint16 aType, nsCString &aResult)
210 {
211     switch (aType) {
212         case nsIDataType::VTYPE_BOOL:
213             aResult.Assign(DBUS_TYPE_BOOLEAN_AS_STRING);
214             return;
215         case nsIDataType::VTYPE_INT8: /* FIXME - check sign issues;
216                                          dbus supports only unsigned 8bit */
217         case nsIDataType::VTYPE_UINT8:
218             aResult.Assign(DBUS_TYPE_BYTE_AS_STRING);
219             return;
220         case nsIDataType::VTYPE_INT16:
221             aResult.Assign(DBUS_TYPE_INT16_AS_STRING);
222             return;
223         case nsIDataType::VTYPE_UINT16:
224             aResult.Assign(DBUS_TYPE_UINT16_AS_STRING);
225             return;
226         case nsIDataType::VTYPE_INT32:
227             aResult.Assign(DBUS_TYPE_INT32_AS_STRING);
228             return;
229         case nsIDataType::VTYPE_UINT32:
230             aResult.Assign(DBUS_TYPE_UINT32_AS_STRING);
231             return;
232         case nsIDataType::VTYPE_INT64:
233             aResult.Assign(DBUS_TYPE_INT64_AS_STRING);
234             return;
235         case nsIDataType::VTYPE_UINT64:
236             aResult.Assign(DBUS_TYPE_UINT64_AS_STRING);
237             return;
238         case nsIDataType::VTYPE_DOUBLE:
239             aResult.Assign(DBUS_TYPE_DOUBLE_AS_STRING);
240             return;
241         case nsIDataType::VTYPE_VOID:
242             // FIXME - assume that string is the best representation
243         case nsIDataType::VTYPE_WSTRING_SIZE_IS:
244         case nsIDataType::VTYPE_WCHAR_STR:
245             aResult.Assign(DBUS_TYPE_STRING_AS_STRING);
246             return;
247         default:
248             BDBLOG(("  getSignatureFromVariantType: %d not a simple type\n", aType));
249             aResult.Assign(DBUS_TYPE_INVALID_AS_STRING);
250     }
251 }
252
253 void
254 getSignatureFromVariant(nsIVariant *aVariant, nsCString &aResult)
255 {
256     aResult.Assign(DBUS_TYPE_INVALID_AS_STRING);
257
258     PRUint16 dataType;
259     aVariant->GetDataType(&dataType);
260
261     switch (dataType) {
262         case nsIDataType::VTYPE_VOID:
263         case nsIDataType::VTYPE_BOOL:
264         case nsIDataType::VTYPE_INT8:
265         case nsIDataType::VTYPE_UINT8:
266         case nsIDataType::VTYPE_INT16:
267         case nsIDataType::VTYPE_UINT16:
268         case nsIDataType::VTYPE_INT32:
269         case nsIDataType::VTYPE_UINT32:
270         case nsIDataType::VTYPE_INT64:
271         case nsIDataType::VTYPE_UINT64:
272         case nsIDataType::VTYPE_DOUBLE:
273         case nsIDataType::VTYPE_WSTRING_SIZE_IS:
274         case nsIDataType::VTYPE_WCHAR_STR:
275         {
276             PRUint32 val = 0;
277             aVariant->GetAsUint32(&val);
278             BDBLOG(("  getSignatureFromVariant: simple type %i:%i\n", dataType, val));
279             getSignatureFromVariantType(dataType, aResult);
280             break;
281         }
282         case nsIDataType::VTYPE_ARRAY:
283         {
284             BDBLOG(("  getSignatureFromVariant: array\n"));
285
286             // need to recurse into array
287             PRUint16 type = 0;
288             nsIID iid;
289             PRUint32 count = 0;
290             void *data_ptr = nsnull;
291
292             aVariant->GetAsArray(&type,
293                                  &iid,
294                                  &count,
295                                  &data_ptr);
296
297             BDBLOG(("  getSignatureFromVariant: got %d elements of type %d\n", count, type));
298
299             nsCAutoString elementsig;
300
301             if (type == nsIDataType::VTYPE_INTERFACE_IS)
302             {
303                 // get element signature from first element
304                 nsISupports *element = ((nsISupports **)data_ptr)[0];
305                 getSignatureFromISupports(element, elementsig);
306             }
307             else if (type == nsIDataType::VTYPE_ARRAY)
308             {
309                 // FIXME - can this happen?
310                 BDBLOG(("    element type array, don't know how to handle\n"));
311             }
312             else
313             {
314                 getSignatureFromVariantType(type, elementsig);
315             }
316
317             aResult.Assign(DBUS_TYPE_ARRAY_AS_STRING);
318             aResult.Append(elementsig);
319
320             nsMemory::Free(data_ptr);
321             break;
322         }
323         case nsIDataType::VTYPE_INTERFACE_IS:
324         {
325             BDBLOG(("  getSignatureFromVariant: interface\n"));
326             nsCOMPtr<nsISupports> is;
327             nsIID *iid;
328             aVariant->GetAsInterface(&iid, getter_AddRefs(is));
329             getSignatureFromISupports(is, aResult);
330             break;
331         }
332         default:
333         {
334             BDBLOG(("  getSignatureFromVariant: unknown type %d\n", dataType));
335             break;
336         }
337     }
338 }
339
340 void
341 getSignatureFromISupports(nsISupports *aISupports, nsCString &aResult)
342 {
343     aResult.Assign(DBUS_TYPE_INVALID_AS_STRING);
344
345     // test argument for nsIVariant
346     nsCOMPtr<nsIVariant> variant = do_QueryInterface(aISupports);
347     if (variant)
348     {
349         BDBLOG(("  getSignatureFromISupports: nsIVariant\n"));
350         getSignatureFromVariant(variant, aResult);
351         return;
352     }
353
354     // test argument for nsIXPConnectWrappedJS
355     nsCOMPtr<nsIXPConnectWrappedJS> wrapped = do_QueryInterface(aISupports);
356     if (wrapped)
357     {
358         BDBLOG(("  getSignatureFromISupports: nsIXPConnectWrappedJS\n"));
359         JSObject *js_obj = nsnull;
360         if (NS_SUCCEEDED(wrapped->GetJSObject(&js_obj)))
361         {
362             // try to get a JS context (code borrowed from xpcsample1.cpp)
363
364             // get the xpconnect service
365             nsresult rv;
366             nsCOMPtr<nsIXPConnect> xpc(do_GetService(nsIXPConnect::GetCID(), &rv));
367             if(NS_FAILED(rv))
368                 return;
369             BDBLOG(("    got nsIXPConnect\n"));
370
371             // get the xpconnect native call context
372             nsAXPCNativeCallContext *callContext = nsnull;
373             xpc->GetCurrentNativeCallContext(&callContext);
374             if(!callContext)
375                 return;
376
377             // Get JSContext of current call
378             JSContext* cx;
379             rv = callContext->GetJSContext(&cx);
380             if(NS_FAILED(rv) || !cx)
381                 return;
382             BDBLOG(("    got JSContext\n"));
383
384             jsval obj_as_jsval = OBJECT_TO_JSVAL(js_obj);
385             getSignatureFromJSValue(cx, &obj_as_jsval, aResult);
386         }
387     }
388 }
389
390 void
391 addVariantToIter(nsIVariant *aVariant, DBusMessageIter *aIter, DBusSignatureIter *aSigIter)
392 {
393     char *element_signature = dbus_signature_iter_get_signature(aSigIter);
394     int element_type = dbus_signature_iter_get_current_type(aSigIter);
395
396     PRUint16 variant_type;
397     aVariant->GetDataType(&variant_type);
398
399     printf("addVariantToIter: signature \"%s\", type %c, variant type: %i\n",
400            element_signature,
401            element_type,
402            variant_type);
403
404     if (dbus_type_is_basic(element_type))
405     {
406         BDBLOG(("  add basic type from variant\n"));
407         addBasicTypeToIter(aVariant, aIter, element_type);
408     }
409     else if (element_type == DBUS_TYPE_ARRAY)
410     {
411         if (dbus_signature_iter_get_element_type(aSigIter) == DBUS_TYPE_DICT_ENTRY)
412         {
413             /* TODO: Support for non-JS Dicts */
414             
415           BDBLOG(("  add dict from variant\n"));
416
417             nsCOMPtr<nsISupports> is;
418             nsIID *iid;
419             // Reported by a leak, dunno why?
420             aVariant->GetAsInterface(&iid, getter_AddRefs(is));
421
422             // test argument for nsIXPConnectWrappedJS
423             nsCOMPtr<nsIXPConnectWrappedJS> wrapped = do_QueryInterface(is);
424             if (wrapped)
425             {
426                 BDBLOG(("  Found XPConnect object\n"));
427                 JSObject *js_obj = nsnull;
428                 if (NS_SUCCEEDED(wrapped->GetJSObject(&js_obj)))
429                 {
430                     // try to get a JS context (code borrowed from xpcsample1.cpp)
431
432                     // get the xpconnect service
433                     nsresult rv;
434                     nsCOMPtr<nsIXPConnect> xpc(do_GetService(nsIXPConnect::GetCID(), &rv));
435                     if(NS_FAILED(rv))
436                         return;
437                     BDBLOG(("    got nsIXPConnect\n"));
438
439                     // get the xpconnect native call context
440                     nsAXPCNativeCallContext *callContext = nsnull;
441                     xpc->GetCurrentNativeCallContext(&callContext);
442                     if(!callContext)
443                         return;
444
445                     // Get JSContext of current call
446                     JSContext* cx;
447                     rv = callContext->GetJSContext(&cx);
448                     if(NS_FAILED(rv) || !cx)
449                         return;
450                     BDBLOG(("    got JSContext\n"));
451
452                     jsval obj_as_jsval = OBJECT_TO_JSVAL(js_obj);
453
454                     // try to enumerate object properties
455                     JSIdArray *props = JS_Enumerate(cx, js_obj);
456                     if (props)
457                     {
458                         BDBLOG(("    got JSIdArray with %i props\n", props->length));
459                         
460                         // Start the array container
461                         DBusMessageIter childIter;
462                         DBusSignatureIter childSigIter;
463                         DBusSignatureIter dictSigIter;
464                         dbus_signature_iter_recurse(aSigIter, &childSigIter);
465                         dbus_message_iter_open_container(aIter, DBUS_TYPE_ARRAY,
466                             dbus_signature_iter_get_signature(&childSigIter),
467                             &childIter);
468
469                         // Skip the dict signature iter to the value type
470                         dbus_signature_iter_recurse(&childSigIter, &dictSigIter);
471                         dbus_signature_iter_next(&dictSigIter); // key type
472
473                         for (int p = 0; p < props->length; p++)
474                         {
475                             // get key signature from first property name
476                             jsval propname;
477                             nsCAutoString tmpsig;
478                             JS_IdToValue(cx, props->vector[p], &propname);
479
480                             // Start the dict container
481                             DBusMessageIter dictIter;
482                             dbus_message_iter_open_container(&childIter, DBUS_TYPE_DICT_ENTRY,
483                                                              NULL, &dictIter);
484
485                             JSString *prop_string = JS_ValueToString(cx, propname);
486                             const char *cstr = NS_ConvertUTF16toUTF8(JS_GetStringChars(prop_string),
487                                                                JS_GetStringLength(prop_string)).get();
488                             // TODO: we only use strings as keys currently, although
489                             // the spec allows any basic type to be a key and we
490                             // probably *could* use the property index.
491                             dbus_message_iter_append_basic(&dictIter,
492                                                            DBUS_TYPE_STRING,
493                                                            &cstr);
494
495                             jsval propvalue;
496                             if (JS_LookupUCProperty(cx,
497                                                     js_obj,
498                                                     JS_GetStringChars(prop_string),
499                                                     JS_GetStringLength(prop_string),
500                                                     &propvalue) == JS_TRUE)
501                             {
502                                 addJSValueToIter(cx, &propvalue, &dictIter, &dictSigIter);
503                             }
504                             
505                             // Close the dict entry container
506                             dbus_message_iter_close_container(&childIter, &dictIter);
507                         }
508
509                         // Close the array container
510                         dbus_message_iter_close_container(aIter, &childIter);
511
512                         JS_DestroyIdArray(cx, props);
513                     }
514                 }
515             }
516         } else {
517           BDBLOG(("  add array from variant\n"));
518
519             // need to recurse into array
520             PRUint16 type = 0;
521             nsIID iid;
522             PRUint32 count = 0;
523             void *data_ptr = nsnull;
524
525             DBusSignatureIter aChildSigIter;
526             dbus_signature_iter_recurse(aSigIter, &aChildSigIter);
527
528             char *array_signature = dbus_signature_iter_get_signature(&aChildSigIter);
529
530             aVariant->GetAsArray(&type,
531                                  &iid,
532                                  &count,
533                                  &data_ptr);
534
535             BDBLOG(("  getSignatureFromVariant: got %d elements of type %d\n", count, type));
536             BDBLOG(("  getSignatureFromVariant: got array signature %s\n", array_signature));
537
538             DBusMessageIter arrayIter;
539             if (dbus_message_iter_open_container(aIter, DBUS_TYPE_ARRAY,
540                                                  array_signature, &arrayIter))
541             {
542                 addArrayDataToIter(data_ptr, count, type, &arrayIter, &aChildSigIter);
543                 dbus_message_iter_close_container(aIter, &arrayIter);
544             }
545
546             nsMemory::Free(data_ptr);
547         }
548     }
549     else if (element_type == DBUS_TYPE_STRUCT)
550     {
551         BDBLOG(("  add struct from variant\n"));
552     }
553     else
554     {
555         BDBLOG(("  unhandled\n"));
556     }
557
558     dbus_free(element_signature);
559 }
560
561 static
562 int is_valid_path (const char *path)
563 {
564   const char *cur = path;
565   const char *prev = cur;
566   
567   if (strlen(path) == 0)
568     return FALSE;
569   
570   /* MUST begin with zero */
571   if (*cur++ != '/')
572     return FALSE;
573   
574   /* The path is guranteed to be null-terminated */
575   while (*cur != '\0')
576   {
577     /* Two slashes can't be together */
578     if (*cur == '/' && *prev == '/')
579     {
580       return FALSE;
581     } else if (!(((*cur) >= '0' && (*cur) <= '9') ||
582                  ((*cur) >= 'A' && (*cur) <= 'Z') ||
583                  ((*cur) >= 'a' && (*cur) <= 'z') ||
584                   (*cur) == '_' || (*cur) == '/')) {
585       return FALSE;
586     }
587     prev = cur;
588     cur++;
589   }
590   
591   return TRUE;
592 }
593
594
595 void addBasicTypeToIter(nsIVariant *aVariant, DBusMessageIter *aIter, int aDBusType)
596 {
597
598     PRUint16 dataType;
599     aVariant->GetDataType(&dataType);
600
601     /* If we got passed an nsISupports, query the variant iface from it and recurse */
602     if (dataType == nsIDataType::VTYPE_INTERFACE_IS)
603     {
604         nsCOMPtr<nsISupports> is;
605         nsIID *iid;
606         if (NS_FAILED(aVariant->GetAsInterface(&iid, getter_AddRefs(is))))
607             return;
608
609         nsCOMPtr<nsIVariant> myVariant = do_QueryInterface(is);
610         if (myVariant)
611             addBasicTypeToIter(myVariant, aIter, aDBusType);
612
613         return;
614     }
615
616     switch (aDBusType)
617     {
618         case DBUS_TYPE_BOOLEAN:
619         {
620             PRBool val;
621             if (NS_FAILED(aVariant->GetAsBool(&val)))
622               return;
623             BDBLOG(("  arg       : BOOLEAN %s\n", val ? "true" : "false"));
624             dbus_message_iter_append_basic(aIter,
625                                            aDBusType,
626                                            &val);
627             break;
628         }
629         case DBUS_TYPE_BYTE:
630         case DBUS_TYPE_INT16:
631         case DBUS_TYPE_UINT16:
632         case DBUS_TYPE_INT32:
633         case DBUS_TYPE_UINT32:
634         {
635             PRUint32 val;
636             if (NS_FAILED(aVariant->GetAsUint32(&val)))
637               return;
638
639             BDBLOG(("  arg       : INT(8|16|32) (%c) %d:%d\n", aDBusType, dataType, val));
640             dbus_message_iter_append_basic(aIter,
641                                            aDBusType,
642                                            &val);
643             break;
644         }
645         case DBUS_TYPE_INT64:
646         {
647             PRInt64 val = 0;
648             if (NS_FAILED(aVariant->GetAsInt64(&val)))
649                 return;
650             BDBLOG(("  arg       : INT64 %lld\n", val));
651             dbus_message_iter_append_basic(aIter,
652                                            aDBusType,
653                                            &val);
654             break;
655         }
656         case DBUS_TYPE_UINT64:
657         {
658             PRUint64 val;
659             if (NS_FAILED(aVariant->GetAsUint64(&val)))
660                 return;
661             BDBLOG(("  arg       : UINT64 %llu\n", val));
662             dbus_message_iter_append_basic(aIter,
663                                            aDBusType,
664                                            &val);
665             break;
666         }
667         case DBUS_TYPE_DOUBLE:
668         {
669             double val;
670             if (NS_FAILED(aVariant->GetAsDouble(&val)))
671                 return;
672             BDBLOG(("  arg       : DOUBLE (%c) %f\n", aDBusType, val));
673             dbus_message_iter_append_basic(aIter,
674                                            aDBusType,
675                                            &val);
676             break;
677         }
678         case DBUS_TYPE_STRING:
679         case DBUS_TYPE_OBJECT_PATH:
680         case DBUS_TYPE_SIGNATURE:
681         {
682             /* FIXME - handle utf-8 conversion */
683             nsCAutoString val;
684             const char *val_ptr;
685             if (NS_FAILED(aVariant->GetAsAUTF8String(val)))
686                 return;
687
688             val_ptr = PromiseFlatCString(val).get();
689             BDBLOG(("  arg       : STRING '%s'\n", val_ptr));
690             if (aDBusType == DBUS_TYPE_OBJECT_PATH
691              && !is_valid_path(val_ptr))
692                 return;
693
694             dbus_message_iter_append_basic(aIter, aDBusType, &val_ptr);
695             break;
696         }
697         default:
698         {
699             BDBLOG(("  addBasicTypeToIter: unhandled DBus type %d!\n", aDBusType));
700             break;
701         }
702     }
703 }
704
705 void addArrayDataToIter(void *data_ptr, PRUint32 count, PRUint16 type, DBusMessageIter *aIter, DBusSignatureIter *aSigIter)
706 {
707     int aDBusType = dbus_signature_iter_get_current_type(aSigIter);
708     BDBLOG(("addArrayDataToIter: appending %d elements of type %d '%c'\n", count, type, aDBusType));
709
710     switch (type)
711     {
712 #define ADD_DATA \
713             for (int i = 0; i < count; i++) \
714                 dbus_message_iter_append_basic(aIter, aDBusType, data+i)
715         case nsIDataType::VTYPE_INT8:
716         case nsIDataType::VTYPE_UINT8:
717         {
718             char *data = (char *)data_ptr;
719             ADD_DATA;
720             break;
721         }
722         case nsIDataType::VTYPE_INT16:
723         case nsIDataType::VTYPE_UINT16:
724         {
725             PRInt16 *data = (PRInt16 *)data_ptr;
726             ADD_DATA;
727             break;
728         }
729         case nsIDataType::VTYPE_INT32:
730         case nsIDataType::VTYPE_UINT32:
731         {
732             PRInt32 *data = (PRInt32 *)data_ptr;
733             ADD_DATA;
734             break;
735         }
736         case nsIDataType::VTYPE_INT64:
737         case nsIDataType::VTYPE_UINT64:
738         {
739             PRInt64 *data = (PRInt64 *)data_ptr;
740             ADD_DATA;
741             break;
742         }
743         case nsIDataType::VTYPE_DOUBLE:
744         {
745             double *data = (double *)data_ptr;
746             ADD_DATA;
747             break;
748         }
749         case nsIDataType::VTYPE_WCHAR_STR:
750         {
751             PRUnichar **data = (PRUnichar **)data_ptr;
752             for (int i = 0; i < count; i++)
753             {
754                 const char *val_ptr;
755                 nsCAutoString val = NS_ConvertUTF16toUTF8(data[i]);
756
757                 val_ptr = PromiseFlatCString(val).get();
758                 BDBLOG(("  arg       : STRING '%s'\n", val_ptr));
759                 if (aDBusType == DBUS_TYPE_OBJECT_PATH
760                  && !is_valid_path(val_ptr))
761                     return;
762
763                 dbus_message_iter_append_basic(aIter, aDBusType, &val_ptr);
764             }
765             break;
766         }
767         case nsIDataType::VTYPE_INTERFACE_IS:
768         {
769             nsISupports **data = (nsISupports **)data_ptr;
770             for (int i = 0; i < count; i++)
771             {
772                 nsCOMPtr<nsIVariant> data_variant = do_QueryInterface(data[i]);
773                 if (data_variant)
774                     addVariantToIter(data_variant, aIter, aSigIter);
775                 else
776                     BDBLOG(("  interface not nsIVariant\n"));
777             }
778             break;
779         }
780         default:
781         {
782             BDBLOG(("addArrayDataToIter: unhandled array data type %d\n", type));
783             break;
784         }
785 #undef ADD_DATA
786     }
787 }
788
789 void
790 addJSValueToIter(JSContext *cx, jsval *aValue, DBusMessageIter *aIter, DBusSignatureIter *aSigIter)
791 {
792
793     int dbusType = dbus_signature_iter_get_current_type(aSigIter);
794
795     BDBLOG(("%s(%c, %s)\n", __FUNCTION__, dbusType,
796            dbus_signature_iter_get_signature(aSigIter)));
797
798     // Using the expected type instead of the actual allows autoconversion
799     switch (dbusType)
800     {
801         case DBUS_TYPE_BOOLEAN:
802         {
803             JSBool b = JS_FALSE;
804             if (JS_ValueToBoolean(cx, *aValue, &b))
805             {
806                 dbus_message_iter_append_basic(aIter, DBUS_TYPE_BOOLEAN, &b);
807             }
808             else
809             {
810                 BDBLOG(("%s(): Could not fetch boolean from jsvalue\n", __FUNCTION__));
811             }
812             
813             
814             break;
815         }
816         case DBUS_TYPE_BYTE:
817         case DBUS_TYPE_INT16:
818         case DBUS_TYPE_UINT16:
819         case DBUS_TYPE_INT32:
820         case DBUS_TYPE_UINT32:
821         case DBUS_TYPE_INT64:
822         case DBUS_TYPE_UINT64:
823         case DBUS_TYPE_DOUBLE:
824         {
825             jsdouble d = 0;
826             
827             if (JS_ValueToNumber(cx, *aValue, &d))
828             {
829                 dbus_message_iter_append_basic(aIter, dbusType, &d);
830             }
831             else
832             {
833                 BDBLOG(("%s(): Could not fetch boolean from jsvalue\n", __FUNCTION__));
834             }
835
836             break;
837         }
838         case DBUS_TYPE_STRING:
839         case DBUS_TYPE_OBJECT_PATH:
840         case DBUS_TYPE_SIGNATURE:
841         {
842             JSString *prop_string = JS_ValueToString(cx, *aValue);
843             const char *cstr = NS_ConvertUTF16toUTF8(JS_GetStringChars(prop_string),
844                                                      JS_GetStringLength(prop_string)).get();
845             dbus_message_iter_append_basic(aIter, dbusType, &cstr);
846             break;
847         }
848         case DBUS_TYPE_ARRAY:
849         {
850             if (JSVAL_IS_OBJECT(*aValue) && JS_IsArrayObject(cx, JSVAL_TO_OBJECT(*aValue)))
851             {
852                 JSIdArray *props = JS_Enumerate(cx, JSVAL_TO_OBJECT(*aValue));
853                 if (props)
854                 {
855                     BDBLOG(("    got JSIdArray\n"));
856
857                     // Open container
858                     DBusSignatureIter arraySigIter;
859                     dbus_signature_iter_recurse(aSigIter, &arraySigIter);
860                     char *array_sig = dbus_signature_iter_get_signature(&arraySigIter);
861                     
862                     DBusMessageIter arrayIter;
863                     dbus_message_iter_open_container(aIter, DBUS_TYPE_ARRAY,
864                                                      array_sig, &arrayIter);
865                     dbus_free(array_sig);
866                     
867                     for (int p = 0; p < props->length; p++)
868                     {
869                         int type = dbus_signature_iter_get_current_type(&arraySigIter);
870                         jsval propname;
871                         JS_IdToValue(cx, props->vector[p], &propname);
872                         
873                         jsval propvalue;
874                         JSString *prop_string = JS_ValueToString(cx, propname);
875                         if (JS_LookupUCProperty(cx,
876                                                 JSVAL_TO_OBJECT(*aValue),
877                                                 JS_GetStringChars(prop_string),
878                                                 JS_GetStringLength(prop_string),
879                                                 &propvalue) == JS_TRUE)
880                         {
881                             addJSValueToIter(cx, &propvalue, &arrayIter, &arraySigIter);
882                         } else {
883                             break;
884                         }
885                         
886                         dbus_signature_iter_next(&arraySigIter);
887                     }
888                     
889                     dbus_message_iter_close_container(aIter, &arrayIter);
890                     JS_DestroyIdArray(cx, props);
891                 }
892             }
893             
894             break;
895         }
896         default:
897             BDBLOG(("Don't know how to convert type '%c'\n", dbus_signature_iter_get_current_type(aSigIter)));
898             break;
899    }
900 }
901
902 void getJSValueFromIter(JSContext* cx, DBusMessageIter *aIter, int aDBusType, jsval *v)
903 {
904
905     switch (aDBusType)
906     {
907         case DBUS_TYPE_STRING:
908         case DBUS_TYPE_OBJECT_PATH:
909         case DBUS_TYPE_SIGNATURE:
910         {
911             char *val = nsnull;
912             dbus_message_iter_get_basic(aIter, &val);
913             if (val != nsnull)
914             {
915                 JSString *str = JS_NewStringCopyN(cx, val, strlen(val));
916                 *v = STRING_TO_JSVAL(str);
917             }
918             break;
919         }
920         case DBUS_TYPE_BYTE:
921         case DBUS_TYPE_INT16:
922         case DBUS_TYPE_UINT16:
923         case DBUS_TYPE_INT32:
924         case DBUS_TYPE_UINT32:
925         case DBUS_TYPE_INT64:
926         case DBUS_TYPE_UINT64:
927         case DBUS_TYPE_DOUBLE:
928         {
929             dbus_uint64_t val = 0;
930             dbus_message_iter_get_basic(aIter, &val);
931             if (!JS_NewNumberValue(cx, (jsdouble)val, v))
932             {
933                 printf("%s: Number conversion from %c failed\n", __FUNCTION__,
934                        aDBusType);
935             }
936             break;
937         }
938         case DBUS_TYPE_ARRAY:
939         {
940             // FIXME: implement!
941 /*            
942             nsTArray<jsval> elems;
943             
944             // recurse to container
945             
946             // iterate over array elements
947             do
948             {
949                 getJSValueFromIter
950                 
951             } while (iter_next);
952
953             // Create an Array object with the elements
954             JSObject *array = JS_NewArrayObject(cx, 0, NULL);
955 */            
956             break;
957         }
958         case DBUS_TYPE_STRUCT:
959         default:
960         {
961             BDBLOG(("%s: Unhandled type %c\n", __FUNCTION__, aDBusType));
962         }
963     }
964
965     return;
966 }
967
968 already_AddRefed<nsIWritableVariant> getVariantFromIter(DBusMessageIter *aIter, int aDBusType)
969 {
970     nsCOMPtr<nsIWritableVariant> variant = do_CreateInstance("@mozilla.org/variant;1");
971     nsIWritableVariant *retval;
972
973     switch (aDBusType)
974     {
975         case DBUS_TYPE_BOOLEAN:
976         {
977             PRUint32 val = 0;
978             BDBLOG(("    arg type BOOLEAN: "));
979             dbus_message_iter_get_basic(aIter, &val);
980             BDBLOG(("%d\n", val));
981             variant->SetAsBool(val);
982             break;
983         }
984
985         case DBUS_TYPE_BYTE:
986         case DBUS_TYPE_INT16:
987         case DBUS_TYPE_UINT16:
988         case DBUS_TYPE_INT32:
989         case DBUS_TYPE_UINT32:
990         {
991             PRUint32 val = 0;
992             BDBLOG(("    arg type INT: "));
993             dbus_message_iter_get_basic(aIter, &val);
994             BDBLOG(("%d\n", val));
995             variant->SetAsUint32(val);
996             break;
997         }
998         case DBUS_TYPE_INT64:
999         {
1000             PRInt64 val;
1001             BDBLOG(("    arg type INT64: "));
1002             dbus_message_iter_get_basic(aIter, &val);
1003             BDBLOG(("%lld\n", val));
1004             variant->SetAsInt64(val);
1005             break;
1006         }
1007         case DBUS_TYPE_UINT64:
1008         {
1009             PRUint64 val;
1010             BDBLOG(("    arg type UINT64: "));
1011             dbus_message_iter_get_basic(aIter, &val);
1012             BDBLOG(("%llu\n", val));
1013             variant->SetAsUint64(val);
1014             break;
1015         }
1016         case DBUS_TYPE_DOUBLE:
1017         {
1018             double val;
1019             BDBLOG(("    arg type DOUBLE: "));
1020             dbus_message_iter_get_basic(aIter, &val);
1021             BDBLOG(("%f\n", val));
1022             variant->SetAsDouble(val);
1023             break;
1024         }
1025         case DBUS_TYPE_STRING:
1026         case DBUS_TYPE_OBJECT_PATH:
1027         case DBUS_TYPE_SIGNATURE:
1028         {
1029             const char *tmp;
1030             BDBLOG(("    arg type STRING/OBJECT_PATH/SIGNATURE: "));
1031             dbus_message_iter_get_basic(aIter, &tmp);
1032             nsDependentCString val(tmp);
1033             BDBLOG(("\"%s\"\n", PromiseFlatCString(val).get()));
1034             variant->SetAsAUTF8String(val);
1035             break;
1036         }
1037         case DBUS_TYPE_ARRAY:
1038         {
1039             if (dbus_message_iter_get_element_type(aIter) == DBUS_TYPE_DICT_ENTRY)
1040             {
1041                 BDBLOG(("    arg type ARRAY with DICT_ENTRY\n"));
1042
1043                 // try to get a JS context (code borrowed from xpcsample1.cpp)
1044
1045                 // get the xpconnect service
1046                 nsresult rv;
1047                 nsCOMPtr<nsIXPConnect> xpc(do_GetService(nsIXPConnect::GetCID(), &rv));
1048                 if(NS_FAILED(rv))
1049                     return nsnull;
1050                 BDBLOG(("    got nsIXPConnect\n"));
1051
1052                 // get the xpconnect native call context
1053                 // FIXME: this doesn't work for signals since there's no
1054                 // active context there...
1055                 nsAXPCNativeCallContext *callContext = nsnull;
1056                 xpc->GetCurrentNativeCallContext(&callContext);
1057                 if(!callContext)
1058                 {
1059                 BDBLOG(("    callContext :(\n"));
1060                     return nsnull;
1061                 }
1062                 // Get JSContext of current call
1063                 JSContext* cx;
1064                 rv = callContext->GetJSContext(&cx);
1065                 if(NS_FAILED(rv) || !cx)
1066                     return nsnull;
1067                 BDBLOG(("    got JSContext\n"));
1068
1069                 // Construct a JS Object
1070                 JSObject *obj = JS_NewObject(cx, nsnull, nsnull, nsnull);
1071                 
1072                 // Set the properties
1073                 DBusMessageIter array_iter;
1074                 dbus_message_iter_recurse(aIter, &array_iter);
1075                 
1076                 do
1077                 {
1078                     DBusMessageIter dict_iter;
1079                     char *key = nsnull;
1080                     dbus_message_iter_recurse(&array_iter, &dict_iter);
1081                     dbus_message_iter_get_basic(&dict_iter, &key);
1082                     BDBLOG(("    found key %s\n", key ? key : "null"));
1083                     dbus_message_iter_next(&dict_iter);
1084                     int value_type = dbus_message_iter_get_arg_type(&dict_iter);
1085                     BDBLOG(("    found value type %c\n", value_type));
1086                     jsval v;
1087                     getJSValueFromIter(cx, &dict_iter, value_type, &v);
1088                     JS_SetProperty(cx, obj, key, &v);
1089                     
1090                 } while (dbus_message_iter_next(&array_iter));
1091                 
1092                 // Convert to variant and return
1093                 nsIVariant *var = nsnull;
1094                 nsresult rs = xpc->JSToVariant(cx, OBJECT_TO_JSVAL(obj), &var);
1095                 variant->SetFromVariant(var);
1096                 var = nsnull;
1097                 NS_ADDREF(retval = variant);
1098                 
1099                 return retval;
1100             }
1101             else
1102             {
1103         
1104                 DBusMessageIter array_iter;
1105                 nsCOMPtr<nsIMutableArray> items;
1106                 PRUint32 item_count;
1107
1108                 BDBLOG(("    arg type ARRAY\n"));
1109                 dbus_message_iter_recurse(aIter, &array_iter);
1110                 items = getArrayFromIter(&array_iter);
1111                 items->GetLength(&item_count);
1112                 BDBLOG(("    array: %d items\n", item_count));
1113
1114                 nsIVariant **item_array = new nsIVariant*[item_count];
1115                 for (int i = 0; i < item_count; i++)
1116                 {
1117                     nsCOMPtr<nsIVariant> item = do_QueryElementAt(items, i);
1118                     item_array[i] = item;
1119                     NS_ADDREF(item);
1120                 }
1121                 variant->SetAsArray(nsIDataType::VTYPE_INTERFACE_IS,
1122                                     &NS_GET_IID(nsIVariant),
1123                                     item_count,
1124                                     item_array);
1125                 for (int i = 0; i < item_count; i++)
1126                     NS_RELEASE(item_array[i]);
1127                 delete[] item_array;
1128             }
1129             break;
1130         }
1131         default:
1132         {
1133             BDBLOG(("    arg type '%c' (%d)\n", aDBusType, aDBusType));
1134             return nsnull;
1135         }
1136     }
1137
1138     NS_ADDREF(retval = variant);
1139     return retval;
1140 }
1141
1142 already_AddRefed<nsIMutableArray> getArrayFromIter(DBusMessageIter *aIter)
1143 {
1144     int current_type;
1145     nsCOMPtr<nsIMutableArray> array = do_CreateInstance("@mozilla.org/array;1");
1146     nsIMutableArray *retval;
1147
1148     BDBLOG(("  ++ enter getArrayFromIter\n"));
1149
1150     while ((current_type = dbus_message_iter_get_arg_type(aIter)) != DBUS_TYPE_INVALID)
1151     {
1152         nsCOMPtr<nsIWritableVariant> variant = getVariantFromIter(aIter, current_type);
1153         if (variant)
1154             array->AppendElement(variant, PR_FALSE);
1155         else
1156             BDBLOG(("    arg type '%c' (%d) not handled\n", current_type, current_type));
1157
1158         dbus_message_iter_next(aIter);
1159     }
1160  
1161     NS_ADDREF(retval = array);
1162     BDBLOG(("  ++ leave getArrayFromIter\n"));
1163     return retval;
1164 }
1165
1166 /* vim: set cindent ts=4 et sw=4: */