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