Bumping manifests a=b2g-bump
[gecko.git] / js / xpconnect / src / XPCConvert.cpp
blob30594954842c9195acf9ce00a9df571ebf879c65
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /* vim: set ts=8 sts=4 et sw=4 tw=99: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 /* Data conversion between native and JavaScript types. */
9 #include "mozilla/ArrayUtils.h"
10 #include "mozilla/Range.h"
12 #include "xpcprivate.h"
13 #include "nsIAtom.h"
14 #include "nsWrapperCache.h"
15 #include "nsJSUtils.h"
16 #include "WrapperFactory.h"
18 #include "nsWrapperCacheInlines.h"
20 #include "jsapi.h"
21 #include "jsfriendapi.h"
22 #include "js/CharacterEncoding.h"
23 #include "jsprf.h"
24 #include "JavaScriptParent.h"
26 #include "mozilla/dom/BindingUtils.h"
27 #include "mozilla/dom/DOMException.h"
28 #include "mozilla/dom/PrimitiveConversions.h"
30 using namespace xpc;
31 using namespace mozilla;
32 using namespace mozilla::dom;
33 using namespace JS;
35 //#define STRICT_CHECK_OF_UNICODE
36 #ifdef STRICT_CHECK_OF_UNICODE
37 #define ILLEGAL_RANGE(c) (0!=((c) & 0xFF80))
38 #else // STRICT_CHECK_OF_UNICODE
39 #define ILLEGAL_RANGE(c) (0!=((c) & 0xFF00))
40 #endif // STRICT_CHECK_OF_UNICODE
42 #define ILLEGAL_CHAR_RANGE(c) (0!=((c) & 0x80))
44 /***********************************************************/
46 // static
47 bool
48 XPCConvert::IsMethodReflectable(const XPTMethodDescriptor& info)
50 if (XPT_MD_IS_NOTXPCOM(info.flags) || XPT_MD_IS_HIDDEN(info.flags))
51 return false;
53 for (int i = info.num_args-1; i >= 0; i--) {
54 const nsXPTParamInfo& param = info.params[i];
55 const nsXPTType& type = param.GetType();
57 // Reflected methods can't use native types. All native types end up
58 // getting tagged as void*, so this check is easy.
59 if (type.TagPart() == nsXPTType::T_VOID)
60 return false;
62 return true;
65 static JSObject*
66 UnwrapNativeCPOW(nsISupports* wrapper)
68 nsCOMPtr<nsIXPConnectWrappedJS> underware = do_QueryInterface(wrapper);
69 if (underware) {
70 JSObject* mainObj = underware->GetJSObject();
71 if (mainObj && mozilla::jsipc::IsWrappedCPOW(mainObj))
72 return mainObj;
74 return nullptr;
77 /***************************************************************************/
79 // static
80 bool
81 XPCConvert::GetISupportsFromJSObject(JSObject* obj, nsISupports** iface)
83 const JSClass* jsclass = js::GetObjectJSClass(obj);
84 MOZ_ASSERT(jsclass, "obj has no class");
85 if (jsclass &&
86 (jsclass->flags & JSCLASS_HAS_PRIVATE) &&
87 (jsclass->flags & JSCLASS_PRIVATE_IS_NSISUPPORTS)) {
88 *iface = (nsISupports*) xpc_GetJSPrivate(obj);
89 return true;
91 *iface = UnwrapDOMObjectToISupports(obj);
92 return !!*iface;
95 /***************************************************************************/
97 // static
98 bool
99 XPCConvert::NativeData2JS(MutableHandleValue d, const void* s,
100 const nsXPTType& type, const nsID* iid, nsresult* pErr)
102 NS_PRECONDITION(s, "bad param");
104 AutoJSContext cx;
105 if (pErr)
106 *pErr = NS_ERROR_XPC_BAD_CONVERT_NATIVE;
108 switch (type.TagPart()) {
109 case nsXPTType::T_I8 :
110 d.setInt32(*static_cast<const int8_t*>(s));
111 return true;
112 case nsXPTType::T_I16 :
113 d.setInt32(*static_cast<const int16_t*>(s));
114 return true;
115 case nsXPTType::T_I32 :
116 d.setInt32(*static_cast<const int32_t*>(s));
117 return true;
118 case nsXPTType::T_I64 :
119 d.setNumber(static_cast<double>(*static_cast<const int64_t*>(s)));
120 return true;
121 case nsXPTType::T_U8 :
122 d.setInt32(*static_cast<const uint8_t*>(s));
123 return true;
124 case nsXPTType::T_U16 :
125 d.setInt32(*static_cast<const uint16_t*>(s));
126 return true;
127 case nsXPTType::T_U32 :
128 d.setNumber(*static_cast<const uint32_t*>(s));
129 return true;
130 case nsXPTType::T_U64 :
131 d.setNumber(static_cast<double>(*static_cast<const uint64_t*>(s)));
132 return true;
133 case nsXPTType::T_FLOAT :
134 d.setNumber(*static_cast<const float*>(s));
135 return true;
136 case nsXPTType::T_DOUBLE:
137 d.setNumber(*static_cast<const double*>(s));
138 return true;
139 case nsXPTType::T_BOOL :
140 d.setBoolean(*static_cast<const bool*>(s));
141 return true;
142 case nsXPTType::T_CHAR :
144 char p = *static_cast<const char*>(s);
146 #ifdef STRICT_CHECK_OF_UNICODE
147 MOZ_ASSERT(! ILLEGAL_CHAR_RANGE(p) , "passing non ASCII data");
148 #endif // STRICT_CHECK_OF_UNICODE
150 JSString* str = JS_NewStringCopyN(cx, &p, 1);
151 if (!str)
152 return false;
154 d.setString(str);
155 return true;
157 case nsXPTType::T_WCHAR :
159 jschar p = *static_cast<const jschar*>(s);
161 JSString* str = JS_NewUCStringCopyN(cx, &p, 1);
162 if (!str)
163 return false;
165 d.setString(str);
166 return true;
169 case nsXPTType::T_JSVAL :
171 d.set(*static_cast<const Value*>(s));
172 return JS_WrapValue(cx, d);
175 case nsXPTType::T_VOID:
176 XPC_LOG_ERROR(("XPCConvert::NativeData2JS : void* params not supported"));
177 return false;
179 case nsXPTType::T_IID:
181 nsID* iid2 = *static_cast<nsID* const*>(s);
182 if (!iid2) {
183 d.setNull();
184 return true;
187 RootedObject scope(cx, JS::CurrentGlobalOrNull(cx));
188 JSObject* obj = xpc_NewIDObject(cx, scope, *iid2);
189 if (!obj)
190 return false;
192 d.setObject(*obj);
193 return true;
196 case nsXPTType::T_ASTRING:
197 // Fall through to T_DOMSTRING case
199 case nsXPTType::T_DOMSTRING:
201 const nsAString* p = *static_cast<const nsAString* const*>(s);
202 if (!p || p->IsVoid()) {
203 d.setNull();
204 return true;
207 nsStringBuffer* buf;
208 if (!XPCStringConvert::ReadableToJSVal(cx, *p, &buf, d))
209 return false;
210 if (buf)
211 buf->AddRef();
212 return true;
215 case nsXPTType::T_CHAR_STR:
217 const char* p = *static_cast<const char* const*>(s);
218 if (!p) {
219 d.setNull();
220 return true;
223 #ifdef STRICT_CHECK_OF_UNICODE
224 bool isAscii = true;
225 for (char* t = p; *t && isAscii; t++) {
226 if (ILLEGAL_CHAR_RANGE(*t))
227 isAscii = false;
229 MOZ_ASSERT(isAscii, "passing non ASCII data");
230 #endif // STRICT_CHECK_OF_UNICODE
232 JSString* str = JS_NewStringCopyZ(cx, p);
233 if (!str)
234 return false;
236 d.setString(str);
237 return true;
240 case nsXPTType::T_WCHAR_STR:
242 const jschar* p = *static_cast<const jschar* const*>(s);
243 if (!p) {
244 d.setNull();
245 return true;
248 JSString* str = JS_NewUCStringCopyZ(cx, p);
249 if (!str)
250 return false;
252 d.setString(str);
253 return true;
255 case nsXPTType::T_UTF8STRING:
257 const nsACString* utf8String = *static_cast<const nsACString* const*>(s);
259 if (!utf8String || utf8String->IsVoid()) {
260 d.setNull();
261 return true;
264 if (utf8String->IsEmpty()) {
265 d.set(JS_GetEmptyStringValue(cx));
266 return true;
269 const uint32_t len = CalcUTF8ToUnicodeLength(*utf8String);
270 // The cString is not empty at this point, but the calculated
271 // UTF-16 length is zero, meaning no valid conversion exists.
272 if (!len)
273 return false;
275 const size_t buffer_size = (len + 1) * sizeof(char16_t);
276 char16_t* buffer =
277 static_cast<char16_t*>(JS_malloc(cx, buffer_size));
278 if (!buffer)
279 return false;
281 uint32_t copied;
282 if (!UTF8ToUnicodeBuffer(*utf8String, buffer, &copied) ||
283 len != copied) {
284 // Copy or conversion during copy failed. Did not copy the
285 // whole string.
286 JS_free(cx, buffer);
287 return false;
290 // JS_NewUCString takes ownership on success, i.e. a
291 // successful call will make it the responsiblity of the JS VM
292 // to free the buffer.
293 JSString* str = JS_NewUCString(cx, buffer, len);
294 if (!str) {
295 JS_free(cx, buffer);
296 return false;
299 d.setString(str);
300 return true;
302 case nsXPTType::T_CSTRING:
304 const nsACString* cString = *static_cast<const nsACString* const*>(s);
306 if (!cString || cString->IsVoid()) {
307 d.setNull();
308 return true;
311 // c-strings (binary blobs) are deliberately not converted from
312 // UTF-8 to UTF-16. T_UTF8Sting is for UTF-8 encoded strings
313 // with automatic conversion.
314 JSString* str = JS_NewStringCopyN(cx, cString->Data(),
315 cString->Length());
316 if (!str)
317 return false;
319 d.setString(str);
320 return true;
323 case nsXPTType::T_INTERFACE:
324 case nsXPTType::T_INTERFACE_IS:
326 nsISupports* iface = *static_cast<nsISupports* const*>(s);
327 if (!iface) {
328 d.setNull();
329 return true;
332 if (iid->Equals(NS_GET_IID(nsIVariant))) {
333 nsCOMPtr<nsIVariant> variant = do_QueryInterface(iface);
334 if (!variant)
335 return false;
337 return XPCVariant::VariantDataToJS(variant,
338 pErr, d);
341 xpcObjectHelper helper(iface);
342 return NativeInterface2JSObject(d, nullptr, helper, iid, nullptr, true, pErr);
345 default:
346 NS_ERROR("bad type");
347 return false;
349 return true;
352 /***************************************************************************/
354 #ifdef DEBUG
355 static bool
356 CheckJSCharInCharRange(jschar c)
358 if (ILLEGAL_RANGE(c)) {
359 /* U+0080/U+0100 - U+FFFF data lost. */
360 static const size_t MSG_BUF_SIZE = 64;
361 char msg[MSG_BUF_SIZE];
362 JS_snprintf(msg, MSG_BUF_SIZE, "jschar out of char range; high bits of data lost: 0x%x", c);
363 NS_WARNING(msg);
364 return false;
367 return true;
370 template<typename CharT>
371 static void
372 CheckCharsInCharRange(const CharT* chars, size_t len)
374 for (size_t i = 0; i < len; i++) {
375 if (!CheckJSCharInCharRange(chars[i]))
376 break;
379 #endif
381 template<typename T>
382 bool ConvertToPrimitive(JSContext* cx, HandleValue v, T* retval)
384 return ValueToPrimitive<T, eDefault>(cx, v, retval);
387 // static
388 bool
389 XPCConvert::JSData2Native(void* d, HandleValue s,
390 const nsXPTType& type,
391 const nsID* iid,
392 nsresult* pErr)
394 NS_PRECONDITION(d, "bad param");
396 AutoJSContext cx;
397 if (pErr)
398 *pErr = NS_ERROR_XPC_BAD_CONVERT_JS;
400 switch (type.TagPart()) {
401 case nsXPTType::T_I8 :
402 return ConvertToPrimitive(cx, s, static_cast<int8_t*>(d));
403 case nsXPTType::T_I16 :
404 return ConvertToPrimitive(cx, s, static_cast<int16_t*>(d));
405 case nsXPTType::T_I32 :
406 return ConvertToPrimitive(cx, s, static_cast<int32_t*>(d));
407 case nsXPTType::T_I64 :
408 return ConvertToPrimitive(cx, s, static_cast<int64_t*>(d));
409 case nsXPTType::T_U8 :
410 return ConvertToPrimitive(cx, s, static_cast<uint8_t*>(d));
411 case nsXPTType::T_U16 :
412 return ConvertToPrimitive(cx, s, static_cast<uint16_t*>(d));
413 case nsXPTType::T_U32 :
414 return ConvertToPrimitive(cx, s, static_cast<uint32_t*>(d));
415 case nsXPTType::T_U64 :
416 return ConvertToPrimitive(cx, s, static_cast<uint64_t*>(d));
417 case nsXPTType::T_FLOAT :
418 return ConvertToPrimitive(cx, s, static_cast<float*>(d));
419 case nsXPTType::T_DOUBLE :
420 return ConvertToPrimitive(cx, s, static_cast<double*>(d));
421 case nsXPTType::T_BOOL :
422 return ConvertToPrimitive(cx, s, static_cast<bool*>(d));
423 case nsXPTType::T_CHAR :
425 JSString* str = ToString(cx, s);
426 if (!str) {
427 return false;
430 jschar ch;
431 if (JS_GetStringLength(str) == 0) {
432 ch = 0;
433 } else {
434 if (!JS_GetStringCharAt(cx, str, 0, &ch))
435 return false;
437 #ifdef DEBUG
438 CheckJSCharInCharRange(ch);
439 #endif
440 *((char*)d) = char(ch);
441 break;
443 case nsXPTType::T_WCHAR :
445 JSString* str;
446 if (!(str = ToString(cx, s))) {
447 return false;
449 size_t length = JS_GetStringLength(str);
450 if (length == 0) {
451 *((uint16_t*)d) = 0;
452 break;
455 jschar ch;
456 if (!JS_GetStringCharAt(cx, str, 0, &ch))
457 return false;
459 *((uint16_t*)d) = uint16_t(ch);
460 break;
462 case nsXPTType::T_JSVAL :
463 *((jsval*)d) = s;
464 break;
465 case nsXPTType::T_VOID:
466 XPC_LOG_ERROR(("XPCConvert::JSData2Native : void* params not supported"));
467 NS_ERROR("void* params not supported");
468 return false;
469 case nsXPTType::T_IID:
471 const nsID* pid = nullptr;
473 // There's no good reason to pass a null IID.
474 if (s.isNullOrUndefined()) {
475 if (pErr)
476 *pErr = NS_ERROR_XPC_BAD_CONVERT_JS;
477 return false;
480 if (!s.isObject() ||
481 (!(pid = xpc_JSObjectToID(cx, &s.toObject()))) ||
482 (!(pid = (const nsID*) nsMemory::Clone(pid, sizeof(nsID))))) {
483 return false;
485 *((const nsID**)d) = pid;
486 return true;
489 case nsXPTType::T_ASTRING:
491 if (s.isUndefined()) {
492 (**((nsAString**)d)).SetIsVoid(true);
493 return true;
495 // Fall through to T_DOMSTRING case.
497 case nsXPTType::T_DOMSTRING:
499 if (s.isNull()) {
500 (**((nsAString**)d)).SetIsVoid(true);
501 return true;
503 size_t length = 0;
504 JSString* str = nullptr;
505 if (!s.isUndefined()) {
506 str = ToString(cx, s);
507 if (!str)
508 return false;
510 length = JS_GetStringLength(str);
511 if (!length) {
512 (**((nsAString**)d)).Truncate();
513 return true;
517 nsAString* ws = *((nsAString**)d);
519 if (!str) {
520 ws->AssignLiteral(MOZ_UTF16("undefined"));
521 } else if (XPCStringConvert::IsDOMString(str)) {
522 // The characters represent an existing nsStringBuffer that
523 // was shared by XPCStringConvert::ReadableToJSVal.
524 const jschar* chars = JS_GetTwoByteExternalStringChars(str);
525 nsStringBuffer::FromData((void*)chars)->ToString(length, *ws);
526 } else if (XPCStringConvert::IsLiteral(str)) {
527 // The characters represent a literal char16_t string constant
528 // compiled into libxul, such as the string "undefined" above.
529 const jschar* chars = JS_GetTwoByteExternalStringChars(str);
530 ws->AssignLiteral(chars, length);
531 } else {
532 if (!AssignJSString(cx, *ws, str))
533 return false;
535 return true;
538 case nsXPTType::T_CHAR_STR:
540 if (s.isUndefined() || s.isNull()) {
541 *((char**)d) = nullptr;
542 return true;
545 JSString* str = ToString(cx, s);
546 if (!str) {
547 return false;
549 #ifdef DEBUG
550 if (JS_StringHasLatin1Chars(str)) {
551 size_t len;
552 AutoCheckCannotGC nogc;
553 const Latin1Char* chars = JS_GetLatin1StringCharsAndLength(cx, nogc, str, &len);
554 if (chars)
555 CheckCharsInCharRange(chars, len);
556 } else {
557 size_t len;
558 AutoCheckCannotGC nogc;
559 const jschar* chars = JS_GetTwoByteStringCharsAndLength(cx, nogc, str, &len);
560 if (chars)
561 CheckCharsInCharRange(chars, len);
563 #endif // DEBUG
564 size_t length = JS_GetStringEncodingLength(cx, str);
565 if (length == size_t(-1)) {
566 return false;
568 char* buffer = static_cast<char*>(nsMemory::Alloc(length + 1));
569 if (!buffer) {
570 return false;
572 JS_EncodeStringToBuffer(cx, str, buffer, length);
573 buffer[length] = '\0';
574 *((void**)d) = buffer;
575 return true;
578 case nsXPTType::T_WCHAR_STR:
580 JSString* str;
582 if (s.isUndefined() || s.isNull()) {
583 *((jschar**)d) = nullptr;
584 return true;
587 if (!(str = ToString(cx, s))) {
588 return false;
590 int len = JS_GetStringLength(str);
591 int byte_len = (len+1)*sizeof(jschar);
592 if (!(*((void**)d) = nsMemory::Alloc(byte_len))) {
593 // XXX should report error
594 return false;
596 mozilla::Range<jschar> destChars(*((jschar**)d), len + 1);
597 if (!JS_CopyStringChars(cx, destChars, str))
598 return false;
599 destChars[len] = 0;
601 return true;
604 case nsXPTType::T_UTF8STRING:
606 if (s.isNull() || s.isUndefined()) {
607 nsCString* rs = *((nsCString**)d);
608 rs->SetIsVoid(true);
609 return true;
612 // The JS val is neither null nor void...
613 JSString* str = ToString(cx, s);
614 if (!str)
615 return false;
617 size_t length = JS_GetStringLength(str);
618 if (!length) {
619 nsCString* rs = *((nsCString**)d);
620 rs->Truncate();
621 return true;
624 JSFlatString* flat = JS_FlattenString(cx, str);
625 if (!flat)
626 return false;
628 size_t utf8Length = JS::GetDeflatedUTF8StringLength(flat);
629 nsACString* rs = *((nsACString**)d);
630 rs->SetLength(utf8Length);
632 JS::DeflateStringToUTF8Buffer(flat, mozilla::RangedPtr<char>(rs->BeginWriting(), utf8Length));
634 return true;
637 case nsXPTType::T_CSTRING:
639 if (s.isNull() || s.isUndefined()) {
640 nsACString* rs = *((nsACString**)d);
641 rs->Truncate();
642 rs->SetIsVoid(true);
643 return true;
646 // The JS val is neither null nor void...
647 JSString* str = ToString(cx, s);
648 if (!str) {
649 return false;
652 size_t length = JS_GetStringEncodingLength(cx, str);
653 if (length == size_t(-1)) {
654 return false;
657 if (!length) {
658 nsCString* rs = *((nsCString**)d);
659 rs->Truncate();
660 return true;
663 nsACString* rs = *((nsACString**)d);
664 rs->SetLength(uint32_t(length));
665 if (rs->Length() != uint32_t(length)) {
666 return false;
668 JS_EncodeStringToBuffer(cx, str, rs->BeginWriting(), length);
670 return true;
673 case nsXPTType::T_INTERFACE:
674 case nsXPTType::T_INTERFACE_IS:
676 MOZ_ASSERT(iid,"can't do interface conversions without iid");
678 if (iid->Equals(NS_GET_IID(nsIVariant))) {
679 nsCOMPtr<nsIVariant> variant = XPCVariant::newVariant(cx, s);
680 if (!variant)
681 return false;
683 variant.forget(static_cast<nsISupports**>(d));
684 return true;
685 } else if (iid->Equals(NS_GET_IID(nsIAtom)) && s.isString()) {
686 // We're trying to pass a string as an nsIAtom. Let's atomize!
687 JSString* str = s.toString();
688 nsAutoJSString autoStr;
689 if (!autoStr.init(cx, str)) {
690 if (pErr)
691 *pErr = NS_ERROR_XPC_BAD_CONVERT_JS_NULL_REF;
692 return false;
694 nsCOMPtr<nsIAtom> atom = NS_NewAtom(autoStr);
695 atom.forget((nsISupports**)d);
696 return true;
698 //else ...
700 if (s.isNullOrUndefined()) {
701 *((nsISupports**)d) = nullptr;
702 return true;
705 // only wrap JSObjects
706 if (!s.isObject()) {
707 if (pErr && s.isInt32() && 0 == s.toInt32())
708 *pErr = NS_ERROR_XPC_BAD_CONVERT_JS_ZERO_ISNOT_NULL;
709 return false;
712 RootedObject src(cx, &s.toObject());
713 return JSObject2NativeInterface((void**)d, src, iid, nullptr, pErr);
715 default:
716 NS_ERROR("bad type");
717 return false;
719 return true;
722 static inline bool
723 CreateHolderIfNeeded(HandleObject obj, MutableHandleValue d,
724 nsIXPConnectJSObjectHolder** dest)
726 if (dest) {
727 nsRefPtr<XPCJSObjectHolder> objHolder = XPCJSObjectHolder::newHolder(obj);
728 if (!objHolder)
729 return false;
731 objHolder.forget(dest);
734 d.setObjectOrNull(obj);
736 return true;
739 /***************************************************************************/
740 // static
741 bool
742 XPCConvert::NativeInterface2JSObject(MutableHandleValue d,
743 nsIXPConnectJSObjectHolder** dest,
744 xpcObjectHelper& aHelper,
745 const nsID* iid,
746 XPCNativeInterface** Interface,
747 bool allowNativeWrapper,
748 nsresult* pErr)
750 MOZ_ASSERT_IF(Interface, iid);
751 if (!iid)
752 iid = &NS_GET_IID(nsISupports);
754 d.setNull();
755 if (dest)
756 *dest = nullptr;
757 if (!aHelper.Object())
758 return true;
759 if (pErr)
760 *pErr = NS_ERROR_XPC_BAD_CONVERT_NATIVE;
762 // We used to have code here that unwrapped and simply exposed the
763 // underlying JSObject. That caused anomolies when JSComponents were
764 // accessed from other JS code - they didn't act like other xpconnect
765 // wrapped components. So, instead, we create "double wrapped" objects
766 // (that means an XPCWrappedNative around an nsXPCWrappedJS). This isn't
767 // optimal -- we could detect this and roll the functionality into a
768 // single wrapper, but the current solution is good enough for now.
769 AutoJSContext cx;
770 XPCWrappedNativeScope* xpcscope = ObjectScope(JS::CurrentGlobalOrNull(cx));
771 if (!xpcscope)
772 return false;
774 // First, see if this object supports the wrapper cache.
775 // Note: If |cache->IsDOMBinding()| is true, then it means that the object
776 // implementing it doesn't want a wrapped native as its JS Object, but
777 // instead it provides its own proxy object. In that case, the object
778 // to use is found as cache->GetWrapper(). If that is null, then the
779 // object will create (and fill the cache) from its WrapObject call.
780 nsWrapperCache* cache = aHelper.GetWrapperCache();
782 RootedObject flat(cx, cache ? cache->GetWrapper() : nullptr);
783 if (!flat && cache && cache->IsDOMBinding()) {
784 RootedObject global(cx, xpcscope->GetGlobalJSObject());
785 js::AssertSameCompartment(cx, global);
786 flat = cache->WrapObject(cx);
787 if (!flat)
788 return false;
790 if (flat) {
791 if (allowNativeWrapper && !JS_WrapObject(cx, &flat))
792 return false;
793 return CreateHolderIfNeeded(flat, d, dest);
796 // Don't double wrap CPOWs. This is a temporary measure for compatibility
797 // with objects that don't provide necessary QIs (such as objects under
798 // the new DOM bindings). We expect the other side of the CPOW to have
799 // the appropriate wrappers in place.
800 RootedObject cpow(cx, UnwrapNativeCPOW(aHelper.Object()));
801 if (cpow) {
802 if (!JS_WrapObject(cx, &cpow))
803 return false;
804 d.setObject(*cpow);
805 return true;
808 // Go ahead and create an XPCWrappedNative for this object.
809 AutoMarkingNativeInterfacePtr iface(cx);
810 if (iid) {
811 if (Interface)
812 iface = *Interface;
814 if (!iface) {
815 iface = XPCNativeInterface::GetNewOrUsed(iid);
816 if (!iface)
817 return false;
819 if (Interface)
820 *Interface = iface;
824 nsRefPtr<XPCWrappedNative> wrapper;
825 nsresult rv = XPCWrappedNative::GetNewOrUsed(aHelper, xpcscope, iface,
826 getter_AddRefs(wrapper));
827 if (NS_FAILED(rv) && pErr)
828 *pErr = rv;
830 // If creating the wrapped native failed, then return early.
831 if (NS_FAILED(rv) || !wrapper)
832 return false;
834 // If we're not creating security wrappers, we can return the
835 // XPCWrappedNative as-is here.
836 flat = wrapper->GetFlatJSObject();
837 if (!allowNativeWrapper) {
838 d.setObjectOrNull(flat);
839 if (dest)
840 wrapper.forget(dest);
841 if (pErr)
842 *pErr = NS_OK;
843 return true;
846 // The call to wrap here handles both cross-compartment and same-compartment
847 // security wrappers.
848 RootedObject original(cx, flat);
849 if (!JS_WrapObject(cx, &flat))
850 return false;
852 d.setObjectOrNull(flat);
854 if (dest) {
855 // The wrapper still holds the original flat object.
856 if (flat == original) {
857 wrapper.forget(dest);
858 } else {
859 nsRefPtr<XPCJSObjectHolder> objHolder =
860 XPCJSObjectHolder::newHolder(flat);
861 if (!objHolder)
862 return false;
864 objHolder.forget(dest);
868 if (pErr)
869 *pErr = NS_OK;
871 return true;
874 /***************************************************************************/
876 // static
877 bool
878 XPCConvert::JSObject2NativeInterface(void** dest, HandleObject src,
879 const nsID* iid,
880 nsISupports* aOuter,
881 nsresult* pErr)
883 MOZ_ASSERT(dest, "bad param");
884 MOZ_ASSERT(src, "bad param");
885 MOZ_ASSERT(iid, "bad param");
887 AutoJSContext cx;
888 JSAutoCompartment ac(cx, src);
890 *dest = nullptr;
891 if (pErr)
892 *pErr = NS_ERROR_XPC_BAD_CONVERT_JS;
894 nsISupports* iface;
896 if (!aOuter) {
897 // Note that if we have a non-null aOuter then it means that we are
898 // forcing the creation of a wrapper even if the object *is* a
899 // wrappedNative or other wise has 'nsISupportness'.
900 // This allows wrapJSAggregatedToNative to work.
902 // If we're looking at a security wrapper, see now if we're allowed to
903 // pass it to C++. If we are, then fall through to the code below. If
904 // we aren't, throw an exception eagerly.
906 // NB: It's very important that we _don't_ unwrap in the aOuter case,
907 // because the caller may explicitly want to create the XPCWrappedJS
908 // around a security wrapper. XBL does this with Xrays from the XBL
909 // scope - see nsBindingManager::GetBindingImplementation.
910 JSObject* inner = js::CheckedUnwrap(src, /* stopAtOuter = */ false);
911 if (!inner) {
912 if (pErr)
913 *pErr = NS_ERROR_XPC_SECURITY_MANAGER_VETO;
914 return false;
917 // Is this really a native xpcom object with a wrapper?
918 XPCWrappedNative* wrappedNative = nullptr;
919 if (IS_WN_REFLECTOR(inner))
920 wrappedNative = XPCWrappedNative::Get(inner);
921 if (wrappedNative) {
922 iface = wrappedNative->GetIdentityObject();
923 return NS_SUCCEEDED(iface->QueryInterface(*iid, dest));
925 // else...
927 // Deal with slim wrappers here.
928 if (GetISupportsFromJSObject(inner ? inner : src, &iface)) {
929 if (iface)
930 return NS_SUCCEEDED(iface->QueryInterface(*iid, dest));
932 return false;
936 // else...
938 nsXPCWrappedJS* wrapper;
939 nsresult rv = nsXPCWrappedJS::GetNewOrUsed(src, *iid, &wrapper);
940 if (pErr)
941 *pErr = rv;
942 if (NS_SUCCEEDED(rv) && wrapper) {
943 // If the caller wanted to aggregate this JS object to a native,
944 // attach it to the wrapper. Note that we allow a maximum of one
945 // aggregated native for a given XPCWrappedJS.
946 if (aOuter)
947 wrapper->SetAggregatedNativeObject(aOuter);
949 // We need to go through the QueryInterface logic to make this return
950 // the right thing for the various 'special' interfaces; e.g.
951 // nsIPropertyBag. We must use AggregatedQueryInterface in cases where
952 // there is an outer to avoid nasty recursion.
953 rv = aOuter ? wrapper->AggregatedQueryInterface(*iid, dest) :
954 wrapper->QueryInterface(*iid, dest);
955 if (pErr)
956 *pErr = rv;
957 NS_RELEASE(wrapper);
958 return NS_SUCCEEDED(rv);
961 // else...
962 return false;
965 /***************************************************************************/
966 /***************************************************************************/
968 // static
969 nsresult
970 XPCConvert::ConstructException(nsresult rv, const char* message,
971 const char* ifaceName, const char* methodName,
972 nsISupports* data,
973 nsIException** exceptn,
974 JSContext* cx,
975 jsval* jsExceptionPtr)
977 MOZ_ASSERT(!cx == !jsExceptionPtr, "Expected cx and jsExceptionPtr to cooccur.");
979 static const char format[] = "\'%s\' when calling method: [%s::%s]";
980 const char * msg = message;
981 nsXPIDLString xmsg;
982 nsAutoCString sxmsg;
984 nsCOMPtr<nsIScriptError> errorObject = do_QueryInterface(data);
985 if (errorObject) {
986 if (NS_SUCCEEDED(errorObject->GetMessageMoz(getter_Copies(xmsg)))) {
987 CopyUTF16toUTF8(xmsg, sxmsg);
988 msg = sxmsg.get();
991 if (!msg)
992 if (!nsXPCException::NameAndFormatForNSResult(rv, nullptr, &msg) || ! msg)
993 msg = "<error>";
995 nsCString msgStr(msg);
996 if (ifaceName && methodName)
997 msgStr.AppendPrintf(format, msg, ifaceName, methodName);
999 nsRefPtr<Exception> e = new Exception(msgStr, rv, EmptyCString(), nullptr, data);
1001 if (cx && jsExceptionPtr) {
1002 e->StowJSVal(*jsExceptionPtr);
1005 e.forget(exceptn);
1006 return NS_OK;
1009 /********************************/
1011 class MOZ_STACK_CLASS AutoExceptionRestorer
1013 public:
1014 AutoExceptionRestorer(JSContext* cx, Value v)
1015 : mContext(cx), tvr(cx, v)
1017 JS_ClearPendingException(mContext);
1020 ~AutoExceptionRestorer()
1022 JS_SetPendingException(mContext, tvr);
1025 private:
1026 JSContext * const mContext;
1027 RootedValue tvr;
1030 // static
1031 nsresult
1032 XPCConvert::JSValToXPCException(MutableHandleValue s,
1033 const char* ifaceName,
1034 const char* methodName,
1035 nsIException** exceptn)
1037 AutoJSContext cx;
1038 AutoExceptionRestorer aer(cx, s);
1040 if (!s.isPrimitive()) {
1041 // we have a JSObject
1042 RootedObject obj(cx, s.toObjectOrNull());
1044 if (!obj) {
1045 NS_ERROR("when is an object not an object?");
1046 return NS_ERROR_FAILURE;
1049 // is this really a native xpcom object with a wrapper?
1050 JSObject* unwrapped = js::CheckedUnwrap(obj, /* stopAtOuter = */ false);
1051 if (!unwrapped)
1052 return NS_ERROR_XPC_SECURITY_MANAGER_VETO;
1053 XPCWrappedNative* wrapper = IS_WN_REFLECTOR(unwrapped) ? XPCWrappedNative::Get(unwrapped)
1054 : nullptr;
1055 if (wrapper) {
1056 nsISupports* supports = wrapper->GetIdentityObject();
1057 nsCOMPtr<nsIException> iface = do_QueryInterface(supports);
1058 if (iface) {
1059 // just pass through the exception (with extra ref and all)
1060 nsCOMPtr<nsIException> temp = iface;
1061 temp.forget(exceptn);
1062 return NS_OK;
1063 } else {
1064 // it is a wrapped native, but not an exception!
1065 return ConstructException(NS_ERROR_XPC_JS_THREW_NATIVE_OBJECT,
1066 nullptr, ifaceName, methodName, supports,
1067 exceptn, nullptr, nullptr);
1069 } else {
1070 // It is a JSObject, but not a wrapped native...
1072 // If it is an engine Error with an error report then let's
1073 // extract the report and build an xpcexception from that
1074 const JSErrorReport* report;
1075 if (nullptr != (report = JS_ErrorFromException(cx, obj))) {
1076 JSAutoByteString message;
1077 JSString* str;
1078 if (nullptr != (str = ToString(cx, s)))
1079 message.encodeLatin1(cx, str);
1080 return JSErrorToXPCException(message.ptr(), ifaceName,
1081 methodName, report, exceptn);
1085 bool found;
1087 // heuristic to see if it might be usable as an xpcexception
1088 if (!JS_HasProperty(cx, obj, "message", &found))
1089 return NS_ERROR_FAILURE;
1091 if (found && !JS_HasProperty(cx, obj, "result", &found))
1092 return NS_ERROR_FAILURE;
1094 if (found) {
1095 // lets try to build a wrapper around the JSObject
1096 nsXPCWrappedJS* jswrapper;
1097 nsresult rv =
1098 nsXPCWrappedJS::GetNewOrUsed(obj, NS_GET_IID(nsIException), &jswrapper);
1099 if (NS_FAILED(rv))
1100 return rv;
1102 *exceptn = static_cast<nsIException*>(jswrapper->GetXPTCStub());
1103 return NS_OK;
1107 // XXX we should do a check against 'js_ErrorClass' here and
1108 // do the right thing - even though it has no JSErrorReport,
1109 // The fact that it is a JSError exceptions means we can extract
1110 // particular info and our 'result' should reflect that.
1112 // otherwise we'll just try to convert it to a string
1114 JSString* str = ToString(cx, s);
1115 if (!str)
1116 return NS_ERROR_FAILURE;
1118 JSAutoByteString strBytes(cx, str);
1119 if (!strBytes)
1120 return NS_ERROR_FAILURE;
1122 return ConstructException(NS_ERROR_XPC_JS_THREW_JS_OBJECT,
1123 strBytes.ptr(), ifaceName, methodName,
1124 nullptr, exceptn, cx, s.address());
1128 if (s.isUndefined() || s.isNull()) {
1129 return ConstructException(NS_ERROR_XPC_JS_THREW_NULL,
1130 nullptr, ifaceName, methodName, nullptr,
1131 exceptn, cx, s.address());
1134 if (s.isNumber()) {
1135 // lets see if it looks like an nsresult
1136 nsresult rv;
1137 double number;
1138 bool isResult = false;
1140 if (s.isInt32()) {
1141 rv = (nsresult) s.toInt32();
1142 if (NS_FAILED(rv))
1143 isResult = true;
1144 else
1145 number = (double) s.toInt32();
1146 } else {
1147 number = s.toDouble();
1148 if (number > 0.0 &&
1149 number < (double)0xffffffff &&
1150 0.0 == fmod(number,1)) {
1151 // Visual Studio 9 doesn't allow casting directly from a
1152 // double to an enumeration type, contrary to 5.2.9(10) of
1153 // C++11, so add an intermediate cast.
1154 rv = (nsresult)(uint32_t) number;
1155 if (NS_FAILED(rv))
1156 isResult = true;
1160 if (isResult)
1161 return ConstructException(rv, nullptr, ifaceName, methodName,
1162 nullptr, exceptn, cx, s.address());
1163 else {
1164 // XXX all this nsISupportsDouble code seems a little redundant
1165 // now that we're storing the jsval in the exception...
1166 nsISupportsDouble* data;
1167 nsCOMPtr<nsIComponentManager> cm;
1168 if (NS_FAILED(NS_GetComponentManager(getter_AddRefs(cm))) || !cm ||
1169 NS_FAILED(cm->CreateInstanceByContractID(NS_SUPPORTS_DOUBLE_CONTRACTID,
1170 nullptr,
1171 NS_GET_IID(nsISupportsDouble),
1172 (void**)&data)))
1173 return NS_ERROR_FAILURE;
1174 data->SetData(number);
1175 rv = ConstructException(NS_ERROR_XPC_JS_THREW_NUMBER, nullptr,
1176 ifaceName, methodName, data, exceptn, cx, s.address());
1177 NS_RELEASE(data);
1178 return rv;
1182 // otherwise we'll just try to convert it to a string
1183 // Note: e.g., bools get converted to JSStrings by this code.
1185 JSString* str = ToString(cx, s);
1186 if (str) {
1187 JSAutoByteString strBytes(cx, str);
1188 if (!!strBytes) {
1189 return ConstructException(NS_ERROR_XPC_JS_THREW_STRING,
1190 strBytes.ptr(), ifaceName, methodName,
1191 nullptr, exceptn, cx, s.address());
1194 return NS_ERROR_FAILURE;
1197 /********************************/
1199 // static
1200 nsresult
1201 XPCConvert::JSErrorToXPCException(const char* message,
1202 const char* ifaceName,
1203 const char* methodName,
1204 const JSErrorReport* report,
1205 nsIException** exceptn)
1207 AutoJSContext cx;
1208 nsresult rv = NS_ERROR_FAILURE;
1209 nsRefPtr<nsScriptError> data;
1210 if (report) {
1211 nsAutoString bestMessage;
1212 if (report && report->ucmessage) {
1213 bestMessage = static_cast<const char16_t*>(report->ucmessage);
1214 } else if (message) {
1215 CopyASCIItoUTF16(message, bestMessage);
1216 } else {
1217 bestMessage.AssignLiteral("JavaScript Error");
1220 const char16_t* uclinebuf =
1221 static_cast<const char16_t*>(report->uclinebuf);
1223 data = new nsScriptError();
1224 data->InitWithWindowID(
1225 bestMessage,
1226 NS_ConvertASCIItoUTF16(report->filename),
1227 uclinebuf ? nsDependentString(uclinebuf) : EmptyString(),
1228 report->lineno,
1229 report->uctokenptr - report->uclinebuf, report->flags,
1230 NS_LITERAL_CSTRING("XPConnect JavaScript"),
1231 nsJSUtils::GetCurrentlyRunningCodeInnerWindowID(cx));
1234 if (data) {
1235 nsAutoCString formattedMsg;
1236 data->ToString(formattedMsg);
1238 rv = ConstructException(NS_ERROR_XPC_JAVASCRIPT_ERROR_WITH_DETAILS,
1239 formattedMsg.get(), ifaceName, methodName,
1240 static_cast<nsIScriptError*>(data.get()),
1241 exceptn, nullptr, nullptr);
1242 } else {
1243 rv = ConstructException(NS_ERROR_XPC_JAVASCRIPT_ERROR,
1244 nullptr, ifaceName, methodName, nullptr,
1245 exceptn, nullptr, nullptr);
1247 return rv;
1250 /***************************************************************************/
1252 // array fun...
1254 #ifdef POPULATE
1255 #undef POPULATE
1256 #endif
1258 // static
1259 bool
1260 XPCConvert::NativeArray2JS(MutableHandleValue d, const void** s,
1261 const nsXPTType& type, const nsID* iid,
1262 uint32_t count, nsresult* pErr)
1264 NS_PRECONDITION(s, "bad param");
1266 AutoJSContext cx;
1268 // XXX add support for putting chars in a string rather than an array
1270 // XXX add support to indicate *which* array element was not convertable
1272 RootedObject array(cx, JS_NewArrayObject(cx, count));
1273 if (!array)
1274 return false;
1276 if (pErr)
1277 *pErr = NS_ERROR_XPC_BAD_CONVERT_NATIVE;
1279 uint32_t i;
1280 RootedValue current(cx, JSVAL_NULL);
1282 #define POPULATE(_t) \
1283 PR_BEGIN_MACRO \
1284 for (i = 0; i < count; i++) { \
1285 if (!NativeData2JS(&current, ((_t*)*s)+i, type, iid, pErr) || \
1286 !JS_SetElement(cx, array, i, current)) \
1287 goto failure; \
1289 PR_END_MACRO
1291 // XXX check IsPtr - esp. to handle array of nsID (as opposed to nsID*)
1293 switch (type.TagPart()) {
1294 case nsXPTType::T_I8 : POPULATE(int8_t); break;
1295 case nsXPTType::T_I16 : POPULATE(int16_t); break;
1296 case nsXPTType::T_I32 : POPULATE(int32_t); break;
1297 case nsXPTType::T_I64 : POPULATE(int64_t); break;
1298 case nsXPTType::T_U8 : POPULATE(uint8_t); break;
1299 case nsXPTType::T_U16 : POPULATE(uint16_t); break;
1300 case nsXPTType::T_U32 : POPULATE(uint32_t); break;
1301 case nsXPTType::T_U64 : POPULATE(uint64_t); break;
1302 case nsXPTType::T_FLOAT : POPULATE(float); break;
1303 case nsXPTType::T_DOUBLE : POPULATE(double); break;
1304 case nsXPTType::T_BOOL : POPULATE(bool); break;
1305 case nsXPTType::T_CHAR : POPULATE(char); break;
1306 case nsXPTType::T_WCHAR : POPULATE(jschar); break;
1307 case nsXPTType::T_VOID : NS_ERROR("bad type"); goto failure;
1308 case nsXPTType::T_IID : POPULATE(nsID*); break;
1309 case nsXPTType::T_DOMSTRING : NS_ERROR("bad type"); goto failure;
1310 case nsXPTType::T_CHAR_STR : POPULATE(char*); break;
1311 case nsXPTType::T_WCHAR_STR : POPULATE(jschar*); break;
1312 case nsXPTType::T_INTERFACE : POPULATE(nsISupports*); break;
1313 case nsXPTType::T_INTERFACE_IS : POPULATE(nsISupports*); break;
1314 case nsXPTType::T_UTF8STRING : NS_ERROR("bad type"); goto failure;
1315 case nsXPTType::T_CSTRING : NS_ERROR("bad type"); goto failure;
1316 case nsXPTType::T_ASTRING : NS_ERROR("bad type"); goto failure;
1317 default : NS_ERROR("bad type"); goto failure;
1320 if (pErr)
1321 *pErr = NS_OK;
1322 d.setObject(*array);
1323 return true;
1325 failure:
1326 return false;
1328 #undef POPULATE
1333 // Check that the tag part of the type matches the type
1334 // of the array. If the check succeeds, check that the size
1335 // of the output does not exceed UINT32_MAX bytes. Allocate
1336 // the memory and copy the elements by memcpy.
1337 static bool
1338 CheckTargetAndPopulate(const nsXPTType& type,
1339 uint8_t requiredType,
1340 size_t typeSize,
1341 uint32_t count,
1342 JSObject* tArr,
1343 void** output,
1344 nsresult* pErr)
1346 // Check that the element type expected by the interface matches
1347 // the type of the elements in the typed array exactly, including
1348 // signedness.
1349 if (type.TagPart() != requiredType) {
1350 if (pErr)
1351 *pErr = NS_ERROR_XPC_BAD_CONVERT_JS;
1353 return false;
1356 // Calulate the maximum number of elements that can fit in
1357 // UINT32_MAX bytes.
1358 size_t max = UINT32_MAX / typeSize;
1360 // This could overflow on 32-bit systems so check max first.
1361 size_t byteSize = count * typeSize;
1362 if (count > max || !(*output = nsMemory::Alloc(byteSize))) {
1363 if (pErr)
1364 *pErr = NS_ERROR_OUT_OF_MEMORY;
1366 return false;
1369 memcpy(*output, JS_GetArrayBufferViewData(tArr), byteSize);
1370 return true;
1373 // Fast conversion of typed arrays to native using memcpy.
1374 // No float or double canonicalization is done. Called by
1375 // JSarray2Native whenever a TypedArray is met. ArrayBuffers
1376 // are not accepted; create a properly typed array view on them
1377 // first. The element type of array must match the XPCOM
1378 // type in size, type and signedness exactly. As an exception,
1379 // Uint8ClampedArray is allowed for arrays of uint8_t. DataViews
1380 // are not supported.
1382 // static
1383 bool
1384 XPCConvert::JSTypedArray2Native(void** d,
1385 JSObject* jsArray,
1386 uint32_t count,
1387 const nsXPTType& type,
1388 nsresult* pErr)
1390 MOZ_ASSERT(jsArray, "bad param");
1391 MOZ_ASSERT(d, "bad param");
1392 MOZ_ASSERT(JS_IsTypedArrayObject(jsArray), "not a typed array");
1394 // Check the actual length of the input array against the
1395 // given size_is.
1396 uint32_t len = JS_GetTypedArrayLength(jsArray);
1397 if (len < count) {
1398 if (pErr)
1399 *pErr = NS_ERROR_XPC_NOT_ENOUGH_ELEMENTS_IN_ARRAY;
1401 return false;
1404 void* output = nullptr;
1406 switch (JS_GetArrayBufferViewType(jsArray)) {
1407 case js::Scalar::Int8:
1408 if (!CheckTargetAndPopulate(nsXPTType::T_I8, type,
1409 sizeof(int8_t), count,
1410 jsArray, &output, pErr)) {
1411 return false;
1413 break;
1415 case js::Scalar::Uint8:
1416 case js::Scalar::Uint8Clamped:
1417 if (!CheckTargetAndPopulate(nsXPTType::T_U8, type,
1418 sizeof(uint8_t), count,
1419 jsArray, &output, pErr)) {
1420 return false;
1422 break;
1424 case js::Scalar::Int16:
1425 if (!CheckTargetAndPopulate(nsXPTType::T_I16, type,
1426 sizeof(int16_t), count,
1427 jsArray, &output, pErr)) {
1428 return false;
1430 break;
1432 case js::Scalar::Uint16:
1433 if (!CheckTargetAndPopulate(nsXPTType::T_U16, type,
1434 sizeof(uint16_t), count,
1435 jsArray, &output, pErr)) {
1436 return false;
1438 break;
1440 case js::Scalar::Int32:
1441 if (!CheckTargetAndPopulate(nsXPTType::T_I32, type,
1442 sizeof(int32_t), count,
1443 jsArray, &output, pErr)) {
1444 return false;
1446 break;
1448 case js::Scalar::Uint32:
1449 if (!CheckTargetAndPopulate(nsXPTType::T_U32, type,
1450 sizeof(uint32_t), count,
1451 jsArray, &output, pErr)) {
1452 return false;
1454 break;
1456 case js::Scalar::Float32:
1457 if (!CheckTargetAndPopulate(nsXPTType::T_FLOAT, type,
1458 sizeof(float), count,
1459 jsArray, &output, pErr)) {
1460 return false;
1462 break;
1464 case js::Scalar::Float64:
1465 if (!CheckTargetAndPopulate(nsXPTType::T_DOUBLE, type,
1466 sizeof(double), count,
1467 jsArray, &output, pErr)) {
1468 return false;
1470 break;
1472 // Yet another array type was defined? It is not supported yet...
1473 default:
1474 if (pErr)
1475 *pErr = NS_ERROR_XPC_BAD_CONVERT_JS;
1477 return false;
1480 *d = output;
1481 if (pErr)
1482 *pErr = NS_OK;
1484 return true;
1487 // static
1488 bool
1489 XPCConvert::JSArray2Native(void** d, HandleValue s,
1490 uint32_t count, const nsXPTType& type,
1491 const nsID* iid, nsresult* pErr)
1493 MOZ_ASSERT(d, "bad param");
1495 AutoJSContext cx;
1497 // XXX add support for getting chars from strings
1499 // XXX add support to indicate *which* array element was not convertable
1501 if (s.isNullOrUndefined()) {
1502 if (0 != count) {
1503 if (pErr)
1504 *pErr = NS_ERROR_XPC_NOT_ENOUGH_ELEMENTS_IN_ARRAY;
1505 return false;
1508 *d = nullptr;
1509 return true;
1512 if (!s.isObject()) {
1513 if (pErr)
1514 *pErr = NS_ERROR_XPC_CANT_CONVERT_PRIMITIVE_TO_ARRAY;
1515 return false;
1518 RootedObject jsarray(cx, &s.toObject());
1520 // If this is a typed array, then try a fast conversion with memcpy.
1521 if (JS_IsTypedArrayObject(jsarray)) {
1522 return JSTypedArray2Native(d, jsarray, count, type, pErr);
1525 if (!JS_IsArrayObject(cx, jsarray)) {
1526 if (pErr)
1527 *pErr = NS_ERROR_XPC_CANT_CONVERT_OBJECT_TO_ARRAY;
1528 return false;
1531 uint32_t len;
1532 if (!JS_GetArrayLength(cx, jsarray, &len) || len < count) {
1533 if (pErr)
1534 *pErr = NS_ERROR_XPC_NOT_ENOUGH_ELEMENTS_IN_ARRAY;
1535 return false;
1538 if (pErr)
1539 *pErr = NS_ERROR_XPC_BAD_CONVERT_JS;
1541 #define POPULATE(_mode, _t) \
1542 PR_BEGIN_MACRO \
1543 cleanupMode = _mode; \
1544 size_t max = UINT32_MAX / sizeof(_t); \
1545 if (count > max || \
1546 nullptr == (array = nsMemory::Alloc(count * sizeof(_t)))) { \
1547 if (pErr) \
1548 *pErr = NS_ERROR_OUT_OF_MEMORY; \
1549 goto failure; \
1551 for (initedCount = 0; initedCount < count; initedCount++) { \
1552 if (!JS_GetElement(cx, jsarray, initedCount, &current) || \
1553 !JSData2Native(((_t*)array)+initedCount, current, type, \
1554 iid, pErr)) \
1555 goto failure; \
1557 PR_END_MACRO
1559 // No Action, FRee memory, RElease object
1560 enum CleanupMode {na, fr, re};
1562 CleanupMode cleanupMode;
1564 void* array = nullptr;
1565 uint32_t initedCount;
1566 RootedValue current(cx);
1568 // XXX check IsPtr - esp. to handle array of nsID (as opposed to nsID*)
1569 // XXX make extra space at end of char* and wchar* and null termintate
1571 switch (type.TagPart()) {
1572 case nsXPTType::T_I8 : POPULATE(na, int8_t); break;
1573 case nsXPTType::T_I16 : POPULATE(na, int16_t); break;
1574 case nsXPTType::T_I32 : POPULATE(na, int32_t); break;
1575 case nsXPTType::T_I64 : POPULATE(na, int64_t); break;
1576 case nsXPTType::T_U8 : POPULATE(na, uint8_t); break;
1577 case nsXPTType::T_U16 : POPULATE(na, uint16_t); break;
1578 case nsXPTType::T_U32 : POPULATE(na, uint32_t); break;
1579 case nsXPTType::T_U64 : POPULATE(na, uint64_t); break;
1580 case nsXPTType::T_FLOAT : POPULATE(na, float); break;
1581 case nsXPTType::T_DOUBLE : POPULATE(na, double); break;
1582 case nsXPTType::T_BOOL : POPULATE(na, bool); break;
1583 case nsXPTType::T_CHAR : POPULATE(na, char); break;
1584 case nsXPTType::T_WCHAR : POPULATE(na, jschar); break;
1585 case nsXPTType::T_VOID : NS_ERROR("bad type"); goto failure;
1586 case nsXPTType::T_IID : POPULATE(fr, nsID*); break;
1587 case nsXPTType::T_DOMSTRING : NS_ERROR("bad type"); goto failure;
1588 case nsXPTType::T_CHAR_STR : POPULATE(fr, char*); break;
1589 case nsXPTType::T_WCHAR_STR : POPULATE(fr, jschar*); break;
1590 case nsXPTType::T_INTERFACE : POPULATE(re, nsISupports*); break;
1591 case nsXPTType::T_INTERFACE_IS : POPULATE(re, nsISupports*); break;
1592 case nsXPTType::T_UTF8STRING : NS_ERROR("bad type"); goto failure;
1593 case nsXPTType::T_CSTRING : NS_ERROR("bad type"); goto failure;
1594 case nsXPTType::T_ASTRING : NS_ERROR("bad type"); goto failure;
1595 default : NS_ERROR("bad type"); goto failure;
1598 *d = array;
1599 if (pErr)
1600 *pErr = NS_OK;
1601 return true;
1603 failure:
1604 // we may need to cleanup the partially filled array of converted stuff
1605 if (array) {
1606 if (cleanupMode == re) {
1607 nsISupports** a = (nsISupports**) array;
1608 for (uint32_t i = 0; i < initedCount; i++) {
1609 nsISupports* p = a[i];
1610 NS_IF_RELEASE(p);
1612 } else if (cleanupMode == fr) {
1613 void** a = (void**) array;
1614 for (uint32_t i = 0; i < initedCount; i++) {
1615 void* p = a[i];
1616 if (p) nsMemory::Free(p);
1619 nsMemory::Free(array);
1622 return false;
1624 #undef POPULATE
1627 // static
1628 bool
1629 XPCConvert::NativeStringWithSize2JS(MutableHandleValue d, const void* s,
1630 const nsXPTType& type,
1631 uint32_t count,
1632 nsresult* pErr)
1634 NS_PRECONDITION(s, "bad param");
1636 AutoJSContext cx;
1637 if (pErr)
1638 *pErr = NS_ERROR_XPC_BAD_CONVERT_NATIVE;
1640 switch (type.TagPart()) {
1641 case nsXPTType::T_PSTRING_SIZE_IS:
1643 char* p = *((char**)s);
1644 if (!p)
1645 break;
1646 JSString* str;
1647 if (!(str = JS_NewStringCopyN(cx, p, count)))
1648 return false;
1649 d.setString(str);
1650 break;
1652 case nsXPTType::T_PWSTRING_SIZE_IS:
1654 jschar* p = *((jschar**)s);
1655 if (!p)
1656 break;
1657 JSString* str;
1658 if (!(str = JS_NewUCStringCopyN(cx, p, count)))
1659 return false;
1660 d.setString(str);
1661 break;
1663 default:
1664 XPC_LOG_ERROR(("XPCConvert::NativeStringWithSize2JS : unsupported type"));
1665 return false;
1667 return true;
1670 // static
1671 bool
1672 XPCConvert::JSStringWithSize2Native(void* d, HandleValue s,
1673 uint32_t count, const nsXPTType& type,
1674 nsresult* pErr)
1676 NS_PRECONDITION(!s.isNull(), "bad param");
1677 NS_PRECONDITION(d, "bad param");
1679 AutoJSContext cx;
1680 uint32_t len;
1682 if (pErr)
1683 *pErr = NS_ERROR_XPC_BAD_CONVERT_NATIVE;
1685 switch (type.TagPart()) {
1686 case nsXPTType::T_PSTRING_SIZE_IS:
1688 if (s.isUndefined() || s.isNull()) {
1689 if (0 != count) {
1690 if (pErr)
1691 *pErr = NS_ERROR_XPC_NOT_ENOUGH_CHARS_IN_STRING;
1692 return false;
1694 if (0 != count) {
1695 len = (count + 1) * sizeof(char);
1696 if (!(*((void**)d) = nsMemory::Alloc(len)))
1697 return false;
1698 return true;
1700 // else ...
1702 *((char**)d) = nullptr;
1703 return true;
1706 JSString* str = ToString(cx, s);
1707 if (!str) {
1708 return false;
1711 size_t length = JS_GetStringEncodingLength(cx, str);
1712 if (length == size_t(-1)) {
1713 return false;
1715 if (length > count) {
1716 if (pErr)
1717 *pErr = NS_ERROR_XPC_NOT_ENOUGH_CHARS_IN_STRING;
1718 return false;
1720 len = uint32_t(length);
1722 if (len < count)
1723 len = count;
1725 uint32_t alloc_len = (len + 1) * sizeof(char);
1726 char* buffer = static_cast<char*>(nsMemory::Alloc(alloc_len));
1727 if (!buffer) {
1728 return false;
1730 JS_EncodeStringToBuffer(cx, str, buffer, len);
1731 buffer[len] = '\0';
1732 *((char**)d) = buffer;
1734 return true;
1737 case nsXPTType::T_PWSTRING_SIZE_IS:
1739 JSString* str;
1741 if (s.isUndefined() || s.isNull()) {
1742 if (0 != count) {
1743 if (pErr)
1744 *pErr = NS_ERROR_XPC_NOT_ENOUGH_CHARS_IN_STRING;
1745 return false;
1748 if (0 != count) {
1749 len = (count + 1) * sizeof(jschar);
1750 if (!(*((void**)d) = nsMemory::Alloc(len)))
1751 return false;
1752 return true;
1755 // else ...
1756 *((const jschar**)d) = nullptr;
1757 return true;
1760 if (!(str = ToString(cx, s))) {
1761 return false;
1764 len = JS_GetStringLength(str);
1765 if (len > count) {
1766 if (pErr)
1767 *pErr = NS_ERROR_XPC_NOT_ENOUGH_CHARS_IN_STRING;
1768 return false;
1771 len = count;
1773 uint32_t alloc_len = (len + 1) * sizeof(jschar);
1774 if (!(*((void**)d) = nsMemory::Alloc(alloc_len))) {
1775 // XXX should report error
1776 return false;
1778 mozilla::Range<jschar> destChars(*((jschar**)d), len + 1);
1779 if (!JS_CopyStringChars(cx, destChars, str))
1780 return false;
1781 destChars[count] = 0;
1783 return true;
1785 default:
1786 XPC_LOG_ERROR(("XPCConvert::JSStringWithSize2Native : unsupported type"));
1787 return false;