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