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