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