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