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