js/xpconnect/src/XPCConvert.cpp

branch
TOR_BUG_3246
changeset 7
129ffea94266
equal deleted inserted replaced
-1:000000000000 0:9a9bfc3437e6
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/. */
6
7 /* Data conversion between native and JavaScript types. */
8
9 #include "mozilla/ArrayUtils.h"
10
11 #include "xpcprivate.h"
12 #include "nsIAtom.h"
13 #include "nsWrapperCache.h"
14 #include "nsJSUtils.h"
15 #include "WrapperFactory.h"
16
17 #include "nsWrapperCacheInlines.h"
18
19 #include "jsapi.h"
20 #include "jsfriendapi.h"
21 #include "jsprf.h"
22 #include "JavaScriptParent.h"
23
24 #include "mozilla/dom/BindingUtils.h"
25 #include "mozilla/dom/DOMException.h"
26 #include "mozilla/dom/PrimitiveConversions.h"
27
28 using namespace xpc;
29 using namespace mozilla;
30 using namespace mozilla::dom;
31 using namespace JS;
32
33 //#define STRICT_CHECK_OF_UNICODE
34 #ifdef STRICT_CHECK_OF_UNICODE
35 #define ILLEGAL_RANGE(c) (0!=((c) & 0xFF80))
36 #else // STRICT_CHECK_OF_UNICODE
37 #define ILLEGAL_RANGE(c) (0!=((c) & 0xFF00))
38 #endif // STRICT_CHECK_OF_UNICODE
39
40 #define ILLEGAL_CHAR_RANGE(c) (0!=((c) & 0x80))
41
42 /***********************************************************/
43
44 // static
45 bool
46 XPCConvert::IsMethodReflectable(const XPTMethodDescriptor& info)
47 {
48 if (XPT_MD_IS_NOTXPCOM(info.flags) || XPT_MD_IS_HIDDEN(info.flags))
49 return false;
50
51 for (int i = info.num_args-1; i >= 0; i--) {
52 const nsXPTParamInfo& param = info.params[i];
53 const nsXPTType& type = param.GetType();
54
55 // Reflected methods can't use native types. All native types end up
56 // getting tagged as void*, so this check is easy.
57 if (type.TagPart() == nsXPTType::T_VOID)
58 return false;
59 }
60 return true;
61 }
62
63 static JSObject*
64 UnwrapNativeCPOW(nsISupports* wrapper)
65 {
66 nsCOMPtr<nsIXPConnectWrappedJS> underware = do_QueryInterface(wrapper);
67 if (underware) {
68 JSObject* mainObj = underware->GetJSObject();
69 if (mainObj && mozilla::jsipc::JavaScriptParent::IsCPOW(mainObj))
70 return mainObj;
71 }
72 return nullptr;
73 }
74
75 /***************************************************************************/
76
77 // static
78 bool
79 XPCConvert::GetISupportsFromJSObject(JSObject* obj, nsISupports** iface)
80 {
81 const JSClass* jsclass = js::GetObjectJSClass(obj);
82 MOZ_ASSERT(jsclass, "obj has no class");
83 if (jsclass &&
84 (jsclass->flags & JSCLASS_HAS_PRIVATE) &&
85 (jsclass->flags & JSCLASS_PRIVATE_IS_NSISUPPORTS)) {
86 *iface = (nsISupports*) xpc_GetJSPrivate(obj);
87 return true;
88 }
89 *iface = UnwrapDOMObjectToISupports(obj);
90 return !!*iface;
91 }
92
93 /***************************************************************************/
94
95 // static
96 bool
97 XPCConvert::NativeData2JS(MutableHandleValue d, const void* s,
98 const nsXPTType& type, const nsID* iid, nsresult* pErr)
99 {
100 NS_PRECONDITION(s, "bad param");
101
102 AutoJSContext cx;
103 if (pErr)
104 *pErr = NS_ERROR_XPC_BAD_CONVERT_NATIVE;
105
106 switch (type.TagPart()) {
107 case nsXPTType::T_I8 :
108 d.setInt32(*static_cast<const int8_t*>(s));
109 return true;
110 case nsXPTType::T_I16 :
111 d.setInt32(*static_cast<const int16_t*>(s));
112 return true;
113 case nsXPTType::T_I32 :
114 d.setInt32(*static_cast<const int32_t*>(s));
115 return true;
116 case nsXPTType::T_I64 :
117 d.setNumber(static_cast<double>(*static_cast<const int64_t*>(s)));
118 return true;
119 case nsXPTType::T_U8 :
120 d.setInt32(*static_cast<const uint8_t*>(s));
121 return true;
122 case nsXPTType::T_U16 :
123 d.setInt32(*static_cast<const uint16_t*>(s));
124 return true;
125 case nsXPTType::T_U32 :
126 d.setNumber(*static_cast<const uint32_t*>(s));
127 return true;
128 case nsXPTType::T_U64 :
129 d.setNumber(static_cast<double>(*static_cast<const uint64_t*>(s)));
130 return true;
131 case nsXPTType::T_FLOAT :
132 d.setNumber(*static_cast<const float*>(s));
133 return true;
134 case nsXPTType::T_DOUBLE:
135 d.setNumber(*static_cast<const double*>(s));
136 return true;
137 case nsXPTType::T_BOOL :
138 d.setBoolean(*static_cast<const bool*>(s));
139 return true;
140 case nsXPTType::T_CHAR :
141 {
142 char p = *static_cast<const char*>(s);
143
144 #ifdef STRICT_CHECK_OF_UNICODE
145 MOZ_ASSERT(! ILLEGAL_CHAR_RANGE(p) , "passing non ASCII data");
146 #endif // STRICT_CHECK_OF_UNICODE
147
148 JSString* str = JS_NewStringCopyN(cx, &p, 1);
149 if (!str)
150 return false;
151
152 d.setString(str);
153 return true;
154 }
155 case nsXPTType::T_WCHAR :
156 {
157 jschar p = *static_cast<const jschar*>(s);
158
159 JSString* str = JS_NewUCStringCopyN(cx, &p, 1);
160 if (!str)
161 return false;
162
163 d.setString(str);
164 return true;
165 }
166
167 case nsXPTType::T_JSVAL :
168 {
169 d.set(*static_cast<const Value*>(s));
170 return JS_WrapValue(cx, d);
171 }
172
173 case nsXPTType::T_VOID:
174 XPC_LOG_ERROR(("XPCConvert::NativeData2JS : void* params not supported"));
175 return false;
176
177 case nsXPTType::T_IID:
178 {
179 nsID* iid2 = *static_cast<nsID* const *>(s);
180 if (!iid2) {
181 d.setNull();
182 return true;
183 }
184
185 RootedObject scope(cx, JS::CurrentGlobalOrNull(cx));
186 JSObject* obj = xpc_NewIDObject(cx, scope, *iid2);
187 if (!obj)
188 return false;
189
190 d.setObject(*obj);
191 return true;
192 }
193
194 case nsXPTType::T_ASTRING:
195 // Fall through to T_DOMSTRING case
196
197 case nsXPTType::T_DOMSTRING:
198 {
199 const nsAString* p = *static_cast<const nsAString* const *>(s);
200 if (!p || p->IsVoid()) {
201 d.setNull();
202 return true;
203 }
204
205 nsStringBuffer* buf;
206 if (!XPCStringConvert::ReadableToJSVal(cx, *p, &buf, d))
207 return false;
208 if (buf)
209 buf->AddRef();
210 return true;
211 }
212
213 case nsXPTType::T_CHAR_STR:
214 {
215 const char* p = *static_cast<const char* const *>(s);
216 if (!p) {
217 d.setNull();
218 return true;
219 }
220
221 #ifdef STRICT_CHECK_OF_UNICODE
222 bool isAscii = true;
223 for (char* t = p; *t && isAscii; t++) {
224 if (ILLEGAL_CHAR_RANGE(*t))
225 isAscii = false;
226 }
227 MOZ_ASSERT(isAscii, "passing non ASCII data");
228 #endif // STRICT_CHECK_OF_UNICODE
229
230 JSString* str = JS_NewStringCopyZ(cx, p);
231 if (!str)
232 return false;
233
234 d.setString(str);
235 return true;
236 }
237
238 case nsXPTType::T_WCHAR_STR:
239 {
240 const jschar* p = *static_cast<const jschar* const *>(s);
241 if (!p) {
242 d.setNull();
243 return true;
244 }
245
246 JSString* str = JS_NewUCStringCopyZ(cx, p);
247 if (!str)
248 return false;
249
250 d.setString(str);
251 return true;
252 }
253 case nsXPTType::T_UTF8STRING:
254 {
255 const nsACString* utf8String = *static_cast<const nsACString* const *>(s);
256
257 if (!utf8String || utf8String->IsVoid()) {
258 d.setNull();
259 return true;
260 }
261
262 if (utf8String->IsEmpty()) {
263 d.set(JS_GetEmptyStringValue(cx));
264 return true;
265 }
266
267 const uint32_t len = CalcUTF8ToUnicodeLength(*utf8String);
268 // The cString is not empty at this point, but the calculated
269 // UTF-16 length is zero, meaning no valid conversion exists.
270 if (!len)
271 return false;
272
273 const size_t buffer_size = (len + 1) * sizeof(char16_t);
274 char16_t* buffer =
275 static_cast<char16_t*>(JS_malloc(cx, buffer_size));
276 if (!buffer)
277 return false;
278
279 uint32_t copied;
280 if (!UTF8ToUnicodeBuffer(*utf8String, buffer, &copied) ||
281 len != copied) {
282 // Copy or conversion during copy failed. Did not copy the
283 // whole string.
284 JS_free(cx, buffer);
285 return false;
286 }
287
288 // JS_NewUCString takes ownership on success, i.e. a
289 // successful call will make it the responsiblity of the JS VM
290 // to free the buffer.
291 JSString* str = JS_NewUCString(cx, buffer, len);
292 if (!str) {
293 JS_free(cx, buffer);
294 return false;
295 }
296
297 d.setString(str);
298 return true;
299 }
300 case nsXPTType::T_CSTRING:
301 {
302 const nsACString* cString = *static_cast<const nsACString* const *>(s);
303
304 if (!cString || cString->IsVoid()) {
305 d.setNull();
306 return true;
307 }
308
309 // c-strings (binary blobs) are deliberately not converted from
310 // UTF-8 to UTF-16. T_UTF8Sting is for UTF-8 encoded strings
311 // with automatic conversion.
312 JSString* str = JS_NewStringCopyN(cx, cString->Data(),
313 cString->Length());
314 if (!str)
315 return false;
316
317 d.setString(str);
318 return true;
319 }
320
321 case nsXPTType::T_INTERFACE:
322 case nsXPTType::T_INTERFACE_IS:
323 {
324 nsISupports* iface = *static_cast<nsISupports* const *>(s);
325 if (!iface) {
326 d.setNull();
327 return true;
328 }
329
330 if (iid->Equals(NS_GET_IID(nsIVariant))) {
331 nsCOMPtr<nsIVariant> variant = do_QueryInterface(iface);
332 if (!variant)
333 return false;
334
335 return XPCVariant::VariantDataToJS(variant,
336 pErr, d);
337 }
338
339 xpcObjectHelper helper(iface);
340 return NativeInterface2JSObject(d, nullptr, helper, iid, nullptr, true, pErr);
341 }
342
343 default:
344 NS_ERROR("bad type");
345 return false;
346 }
347 return true;
348 }
349
350 /***************************************************************************/
351
352 #ifdef DEBUG
353 static bool
354 CheckJSCharInCharRange(jschar c)
355 {
356 if (ILLEGAL_RANGE(c)) {
357 /* U+0080/U+0100 - U+FFFF data lost. */
358 static const size_t MSG_BUF_SIZE = 64;
359 char msg[MSG_BUF_SIZE];
360 JS_snprintf(msg, MSG_BUF_SIZE, "jschar out of char range; high bits of data lost: 0x%x", c);
361 NS_WARNING(msg);
362 return false;
363 }
364
365 return true;
366 }
367 #endif
368
369 template<typename T>
370 bool ConvertToPrimitive(JSContext *cx, HandleValue v, T *retval)
371 {
372 return ValueToPrimitive<T, eDefault>(cx, v, retval);
373 }
374
375 // static
376 bool
377 XPCConvert::JSData2Native(void* d, HandleValue s,
378 const nsXPTType& type,
379 bool useAllocator, const nsID* iid,
380 nsresult* pErr)
381 {
382 NS_PRECONDITION(d, "bad param");
383
384 AutoJSContext cx;
385 if (pErr)
386 *pErr = NS_ERROR_XPC_BAD_CONVERT_JS;
387
388 switch (type.TagPart()) {
389 case nsXPTType::T_I8 :
390 return ConvertToPrimitive(cx, s, static_cast<int8_t*>(d));
391 case nsXPTType::T_I16 :
392 return ConvertToPrimitive(cx, s, static_cast<int16_t*>(d));
393 case nsXPTType::T_I32 :
394 return ConvertToPrimitive(cx, s, static_cast<int32_t*>(d));
395 case nsXPTType::T_I64 :
396 return ConvertToPrimitive(cx, s, static_cast<int64_t*>(d));
397 case nsXPTType::T_U8 :
398 return ConvertToPrimitive(cx, s, static_cast<uint8_t*>(d));
399 case nsXPTType::T_U16 :
400 return ConvertToPrimitive(cx, s, static_cast<uint16_t*>(d));
401 case nsXPTType::T_U32 :
402 return ConvertToPrimitive(cx, s, static_cast<uint32_t*>(d));
403 case nsXPTType::T_U64 :
404 return ConvertToPrimitive(cx, s, static_cast<uint64_t*>(d));
405 case nsXPTType::T_FLOAT :
406 return ConvertToPrimitive(cx, s, static_cast<float*>(d));
407 case nsXPTType::T_DOUBLE :
408 return ConvertToPrimitive(cx, s, static_cast<double*>(d));
409 case nsXPTType::T_BOOL :
410 return ConvertToPrimitive(cx, s, static_cast<bool*>(d));
411 case nsXPTType::T_CHAR :
412 {
413 JSString* str = ToString(cx, s);
414 if (!str) {
415 return false;
416 }
417 size_t length;
418 const jschar* chars = JS_GetStringCharsAndLength(cx, str, &length);
419 if (!chars) {
420 return false;
421 }
422 jschar ch = length ? chars[0] : 0;
423 #ifdef DEBUG
424 CheckJSCharInCharRange(ch);
425 #endif
426 *((char*)d) = char(ch);
427 break;
428 }
429 case nsXPTType::T_WCHAR :
430 {
431 JSString* str;
432 if (!(str = ToString(cx, s))) {
433 return false;
434 }
435 size_t length;
436 const jschar* chars = JS_GetStringCharsAndLength(cx, str, &length);
437 if (!chars) {
438 return false;
439 }
440 if (length == 0) {
441 *((uint16_t*)d) = 0;
442 break;
443 }
444 *((uint16_t*)d) = uint16_t(chars[0]);
445 break;
446 }
447 case nsXPTType::T_JSVAL :
448 *((jsval*)d) = s;
449 break;
450 case nsXPTType::T_VOID:
451 XPC_LOG_ERROR(("XPCConvert::JSData2Native : void* params not supported"));
452 NS_ERROR("void* params not supported");
453 return false;
454 case nsXPTType::T_IID:
455 {
456 const nsID* pid = nullptr;
457
458 // There's no good reason to pass a null IID.
459 if (s.isNullOrUndefined()) {
460 if (pErr)
461 *pErr = NS_ERROR_XPC_BAD_CONVERT_JS;
462 return false;
463 }
464
465 if (!s.isObject() ||
466 (!(pid = xpc_JSObjectToID(cx, &s.toObject()))) ||
467 (!(pid = (const nsID*) nsMemory::Clone(pid, sizeof(nsID))))) {
468 return false;
469 }
470 *((const nsID**)d) = pid;
471 return true;
472 }
473
474 case nsXPTType::T_ASTRING:
475 {
476 if (JSVAL_IS_VOID(s)) {
477 if (useAllocator)
478 *((const nsAString**)d) = &NullString();
479 else
480 (**((nsAString**)d)).SetIsVoid(true);
481 return true;
482 }
483 // Fall through to T_DOMSTRING case.
484 }
485 case nsXPTType::T_DOMSTRING:
486 {
487 if (JSVAL_IS_NULL(s)) {
488 if (useAllocator)
489 *((const nsAString**)d) = &NullString();
490 else
491 (**((nsAString**)d)).SetIsVoid(true);
492 return true;
493 }
494 size_t length = 0;
495 const char16_t* chars = nullptr;
496 JSString* str = nullptr;
497 if (!JSVAL_IS_VOID(s)) {
498 str = ToString(cx, s);
499 if (!str)
500 return false;
501
502 chars = useAllocator ? JS_GetStringCharsZAndLength(cx, str, &length)
503 : JS_GetStringCharsAndLength(cx, str, &length);
504 if (!chars)
505 return false;
506
507 if (!length) {
508 if (useAllocator)
509 *((const nsAString**)d) = &EmptyString();
510 else
511 (**((nsAString**)d)).Truncate();
512 return true;
513 }
514 }
515
516 nsString* ws;
517 if (useAllocator) {
518 ws = nsXPConnect::GetRuntimeInstance()->NewShortLivedString();
519 *((const nsString**)d) = ws;
520 } else {
521 ws = *((nsString**)d);
522 }
523
524 if (!str) {
525 ws->AssignLiteral(MOZ_UTF16("undefined"));
526 } else if (XPCStringConvert::IsDOMString(str)) {
527 // The characters represent an existing nsStringBuffer that
528 // was shared by XPCStringConvert::ReadableToJSVal.
529 nsStringBuffer::FromData((void *)chars)->ToString(length, *ws);
530 } else if (XPCStringConvert::IsLiteral(str)) {
531 // The characters represent a literal char16_t string constant
532 // compiled into libxul, such as the string "undefined" above.
533 ws->AssignLiteral(chars, length);
534 } else if (useAllocator && STRING_TO_JSVAL(str) == s) {
535 // The JS string will exist over the function call.
536 // We don't need to copy the characters in this case.
537 ws->Rebind(chars, length);
538 } else {
539 ws->Assign(chars, length);
540 }
541 return true;
542 }
543
544 case nsXPTType::T_CHAR_STR:
545 {
546 if (JSVAL_IS_VOID(s) || JSVAL_IS_NULL(s)) {
547 *((char**)d) = nullptr;
548 return true;
549 }
550
551 JSString* str = ToString(cx, s);
552 if (!str) {
553 return false;
554 }
555 #ifdef DEBUG
556 const jschar* chars=nullptr;
557 if (nullptr != (chars = JS_GetStringCharsZ(cx, str))) {
558 bool legalRange = true;
559 int len = JS_GetStringLength(str);
560 const jschar* t;
561 int32_t i=0;
562 for (t=chars; (i< len) && legalRange ; i++,t++) {
563 if (!CheckJSCharInCharRange(*t))
564 break;
565 }
566 }
567 #endif // DEBUG
568 size_t length = JS_GetStringEncodingLength(cx, str);
569 if (length == size_t(-1)) {
570 return false;
571 }
572 char *buffer = static_cast<char *>(nsMemory::Alloc(length + 1));
573 if (!buffer) {
574 return false;
575 }
576 JS_EncodeStringToBuffer(cx, str, buffer, length);
577 buffer[length] = '\0';
578 *((void**)d) = buffer;
579 return true;
580 }
581
582 case nsXPTType::T_WCHAR_STR:
583 {
584 const jschar* chars=nullptr;
585 JSString* str;
586
587 if (JSVAL_IS_VOID(s) || JSVAL_IS_NULL(s)) {
588 *((jschar**)d) = nullptr;
589 return true;
590 }
591
592 if (!(str = ToString(cx, s))) {
593 return false;
594 }
595 if (!(chars = JS_GetStringCharsZ(cx, str))) {
596 return false;
597 }
598 int len = JS_GetStringLength(str);
599 int byte_len = (len+1)*sizeof(jschar);
600 if (!(*((void**)d) = nsMemory::Alloc(byte_len))) {
601 // XXX should report error
602 return false;
603 }
604 jschar* destchars = *((jschar**)d);
605 memcpy(destchars, chars, byte_len);
606 destchars[len] = 0;
607
608 return true;
609 }
610
611 case nsXPTType::T_UTF8STRING:
612 {
613 const jschar* chars;
614 size_t length;
615 JSString* str;
616
617 if (JSVAL_IS_NULL(s) || JSVAL_IS_VOID(s)) {
618 if (useAllocator) {
619 *((const nsACString**)d) = &NullCString();
620 } else {
621 nsCString* rs = *((nsCString**)d);
622 rs->SetIsVoid(true);
623 }
624 return true;
625 }
626
627 // The JS val is neither null nor void...
628
629 if (!(str = ToString(cx, s))||
630 !(chars = JS_GetStringCharsAndLength(cx, str, &length))) {
631 return false;
632 }
633
634 if (!length) {
635 if (useAllocator) {
636 *((const nsACString**)d) = &EmptyCString();
637 } else {
638 nsCString* rs = *((nsCString**)d);
639 rs->Truncate();
640 }
641 return true;
642 }
643
644 nsCString *rs;
645 if (useAllocator) {
646 // Use nsCString to enable sharing
647 rs = new nsCString();
648 if (!rs)
649 return false;
650
651 *((const nsCString**)d) = rs;
652 } else {
653 rs = *((nsCString**)d);
654 }
655 CopyUTF16toUTF8(Substring(chars, length), *rs);
656 return true;
657 }
658
659 case nsXPTType::T_CSTRING:
660 {
661 if (JSVAL_IS_NULL(s) || JSVAL_IS_VOID(s)) {
662 if (useAllocator) {
663 nsACString *rs = new nsCString();
664 if (!rs)
665 return false;
666
667 rs->SetIsVoid(true);
668 *((nsACString**)d) = rs;
669 } else {
670 nsACString* rs = *((nsACString**)d);
671 rs->Truncate();
672 rs->SetIsVoid(true);
673 }
674 return true;
675 }
676
677 // The JS val is neither null nor void...
678 JSString* str = ToString(cx, s);
679 if (!str) {
680 return false;
681 }
682
683 size_t length = JS_GetStringEncodingLength(cx, str);
684 if (length == size_t(-1)) {
685 return false;
686 }
687
688 if (!length) {
689 if (useAllocator) {
690 *((const nsACString**)d) = &EmptyCString();
691 } else {
692 nsCString* rs = *((nsCString**)d);
693 rs->Truncate();
694 }
695 return true;
696 }
697
698 nsACString *rs;
699 if (useAllocator) {
700 rs = new nsCString();
701 if (!rs)
702 return false;
703 *((const nsACString**)d) = rs;
704 } else {
705 rs = *((nsACString**)d);
706 }
707
708 rs->SetLength(uint32_t(length));
709 if (rs->Length() != uint32_t(length)) {
710 return false;
711 }
712 JS_EncodeStringToBuffer(cx, str, rs->BeginWriting(), length);
713
714 return true;
715 }
716
717 case nsXPTType::T_INTERFACE:
718 case nsXPTType::T_INTERFACE_IS:
719 {
720 MOZ_ASSERT(iid,"can't do interface conversions without iid");
721
722 if (iid->Equals(NS_GET_IID(nsIVariant))) {
723 nsCOMPtr<nsIVariant> variant = XPCVariant::newVariant(cx, s);
724 if (!variant)
725 return false;
726
727 variant.forget(static_cast<nsISupports**>(d));
728 return true;
729 } else if (iid->Equals(NS_GET_IID(nsIAtom)) &&
730 JSVAL_IS_STRING(s)) {
731 // We're trying to pass a string as an nsIAtom. Let's atomize!
732 JSString* str = JSVAL_TO_STRING(s);
733 const char16_t* chars = JS_GetStringCharsZ(cx, str);
734 if (!chars) {
735 if (pErr)
736 *pErr = NS_ERROR_XPC_BAD_CONVERT_JS_NULL_REF;
737 return false;
738 }
739 uint32_t length = JS_GetStringLength(str);
740 nsCOMPtr<nsIAtom> atom =
741 NS_NewAtom(nsDependentSubstring(chars, chars + length));
742 atom.forget((nsISupports**)d);
743 return true;
744 }
745 //else ...
746
747 if (s.isNullOrUndefined()) {
748 *((nsISupports**)d) = nullptr;
749 return true;
750 }
751
752 // only wrap JSObjects
753 if (!s.isObject()) {
754 if (pErr && s.isInt32() && 0 == s.toInt32())
755 *pErr = NS_ERROR_XPC_BAD_CONVERT_JS_ZERO_ISNOT_NULL;
756 return false;
757 }
758
759 RootedObject src(cx, &s.toObject());
760 return JSObject2NativeInterface((void**)d, src, iid, nullptr, pErr);
761 }
762 default:
763 NS_ERROR("bad type");
764 return false;
765 }
766 return true;
767 }
768
769 static inline bool
770 CreateHolderIfNeeded(HandleObject obj, MutableHandleValue d,
771 nsIXPConnectJSObjectHolder** dest)
772 {
773 if (dest) {
774 nsRefPtr<XPCJSObjectHolder> objHolder = XPCJSObjectHolder::newHolder(obj);
775 if (!objHolder)
776 return false;
777
778 objHolder.forget(dest);
779 }
780
781 d.setObjectOrNull(obj);
782
783 return true;
784 }
785
786 /***************************************************************************/
787 // static
788 bool
789 XPCConvert::NativeInterface2JSObject(MutableHandleValue d,
790 nsIXPConnectJSObjectHolder** dest,
791 xpcObjectHelper& aHelper,
792 const nsID* iid,
793 XPCNativeInterface** Interface,
794 bool allowNativeWrapper,
795 nsresult* pErr)
796 {
797 MOZ_ASSERT_IF(Interface, iid);
798 if (!iid)
799 iid = &NS_GET_IID(nsISupports);
800
801 d.setNull();
802 if (dest)
803 *dest = nullptr;
804 if (!aHelper.Object())
805 return true;
806 if (pErr)
807 *pErr = NS_ERROR_XPC_BAD_CONVERT_NATIVE;
808
809 // We used to have code here that unwrapped and simply exposed the
810 // underlying JSObject. That caused anomolies when JSComponents were
811 // accessed from other JS code - they didn't act like other xpconnect
812 // wrapped components. So, instead, we create "double wrapped" objects
813 // (that means an XPCWrappedNative around an nsXPCWrappedJS). This isn't
814 // optimal -- we could detect this and roll the functionality into a
815 // single wrapper, but the current solution is good enough for now.
816 AutoJSContext cx;
817 XPCWrappedNativeScope* xpcscope = GetObjectScope(JS::CurrentGlobalOrNull(cx));
818 if (!xpcscope)
819 return false;
820
821 // First, see if this object supports the wrapper cache.
822 // Note: If |cache->IsDOMBinding()| is true, then it means that the object
823 // implementing it doesn't want a wrapped native as its JS Object, but
824 // instead it provides its own proxy object. In that case, the object
825 // to use is found as cache->GetWrapper(). If that is null, then the
826 // object will create (and fill the cache) from its WrapObject call.
827 nsWrapperCache *cache = aHelper.GetWrapperCache();
828
829 RootedObject flat(cx, cache ? cache->GetWrapper() : nullptr);
830 if (!flat && cache && cache->IsDOMBinding()) {
831 RootedObject global(cx, xpcscope->GetGlobalJSObject());
832 js::AssertSameCompartment(cx, global);
833 flat = cache->WrapObject(cx);
834 if (!flat)
835 return false;
836 }
837 if (flat) {
838 if (allowNativeWrapper && !JS_WrapObject(cx, &flat))
839 return false;
840 return CreateHolderIfNeeded(flat, d, dest);
841 }
842
843 // Don't double wrap CPOWs. This is a temporary measure for compatibility
844 // with objects that don't provide necessary QIs (such as objects under
845 // the new DOM bindings). We expect the other side of the CPOW to have
846 // the appropriate wrappers in place.
847 RootedObject cpow(cx, UnwrapNativeCPOW(aHelper.Object()));
848 if (cpow) {
849 if (!JS_WrapObject(cx, &cpow))
850 return false;
851 d.setObject(*cpow);
852 return true;
853 }
854
855 // We can't simply construct a slim wrapper. Go ahead and create an
856 // XPCWrappedNative for this object. At this point, |flat| could be
857 // non-null, meaning that either we already have a wrapped native from
858 // the cache (which might need to be QI'd to the new interface) or that
859 // we found a slim wrapper that we'll have to morph.
860 AutoMarkingNativeInterfacePtr iface(cx);
861 if (iid) {
862 if (Interface)
863 iface = *Interface;
864
865 if (!iface) {
866 iface = XPCNativeInterface::GetNewOrUsed(iid);
867 if (!iface)
868 return false;
869
870 if (Interface)
871 *Interface = iface;
872 }
873 }
874
875 MOZ_ASSERT(!flat || IS_WN_REFLECTOR(flat), "What kind of wrapper is this?");
876
877 nsresult rv;
878 XPCWrappedNative* wrapper;
879 nsRefPtr<XPCWrappedNative> strongWrapper;
880 if (!flat) {
881 rv = XPCWrappedNative::GetNewOrUsed(aHelper, xpcscope, iface,
882 getter_AddRefs(strongWrapper));
883
884 wrapper = strongWrapper;
885 } else {
886 MOZ_ASSERT(IS_WN_REFLECTOR(flat));
887
888 wrapper = XPCWrappedNative::Get(flat);
889
890 // If asked to return the wrapper we'll return a strong reference,
891 // otherwise we'll just return its JSObject in d (which should be
892 // rooted in that case).
893 if (dest)
894 strongWrapper = wrapper;
895 if (iface)
896 wrapper->FindTearOff(iface, false, &rv);
897 else
898 rv = NS_OK;
899 }
900
901 if (NS_FAILED(rv) && pErr)
902 *pErr = rv;
903
904 // If creating the wrapped native failed, then return early.
905 if (NS_FAILED(rv) || !wrapper)
906 return false;
907
908 // If we're not creating security wrappers, we can return the
909 // XPCWrappedNative as-is here.
910 flat = wrapper->GetFlatJSObject();
911 jsval v = OBJECT_TO_JSVAL(flat);
912 if (!allowNativeWrapper) {
913 d.set(v);
914 if (dest)
915 strongWrapper.forget(dest);
916 if (pErr)
917 *pErr = NS_OK;
918 return true;
919 }
920
921 // The call to wrap here handles both cross-compartment and same-compartment
922 // security wrappers.
923 RootedObject original(cx, flat);
924 if (!JS_WrapObject(cx, &flat))
925 return false;
926
927 d.setObjectOrNull(flat);
928
929 if (dest) {
930 // The strongWrapper still holds the original flat object.
931 if (flat == original) {
932 strongWrapper.forget(dest);
933 } else {
934 nsRefPtr<XPCJSObjectHolder> objHolder =
935 XPCJSObjectHolder::newHolder(flat);
936 if (!objHolder)
937 return false;
938
939 objHolder.forget(dest);
940 }
941 }
942
943 if (pErr)
944 *pErr = NS_OK;
945
946 return true;
947 }
948
949 /***************************************************************************/
950
951 // static
952 bool
953 XPCConvert::JSObject2NativeInterface(void** dest, HandleObject src,
954 const nsID* iid,
955 nsISupports* aOuter,
956 nsresult* pErr)
957 {
958 MOZ_ASSERT(dest, "bad param");
959 MOZ_ASSERT(src, "bad param");
960 MOZ_ASSERT(iid, "bad param");
961
962 AutoJSContext cx;
963 JSAutoCompartment ac(cx, src);
964
965 *dest = nullptr;
966 if (pErr)
967 *pErr = NS_ERROR_XPC_BAD_CONVERT_JS;
968
969 nsISupports* iface;
970
971 if (!aOuter) {
972 // Note that if we have a non-null aOuter then it means that we are
973 // forcing the creation of a wrapper even if the object *is* a
974 // wrappedNative or other wise has 'nsISupportness'.
975 // This allows wrapJSAggregatedToNative to work.
976
977 // If we're looking at a security wrapper, see now if we're allowed to
978 // pass it to C++. If we are, then fall through to the code below. If
979 // we aren't, throw an exception eagerly.
980 //
981 // NB: It's very important that we _don't_ unwrap in the aOuter case,
982 // because the caller may explicitly want to create the XPCWrappedJS
983 // around a security wrapper. XBL does this with Xrays from the XBL
984 // scope - see nsBindingManager::GetBindingImplementation.
985 JSObject* inner = js::CheckedUnwrap(src, /* stopAtOuter = */ false);
986
987 // Hack - For historical reasons, wrapped chrome JS objects have been
988 // passable as native interfaces. We'd like to fix this, but it
989 // involves fixing the contacts API and PeerConnection to stop using
990 // COWs. This needs to happen, but for now just preserve the old
991 // behavior.
992 //
993 // Note that there is an identical hack in getWrapper which should be
994 // removed if this one is.
995 if (!inner && MOZ_UNLIKELY(xpc::WrapperFactory::IsCOW(src)))
996 inner = js::UncheckedUnwrap(src);
997 if (!inner) {
998 if (pErr)
999 *pErr = NS_ERROR_XPC_SECURITY_MANAGER_VETO;
1000 return false;
1001 }
1002
1003 // Is this really a native xpcom object with a wrapper?
1004 XPCWrappedNative* wrappedNative = nullptr;
1005 if (IS_WN_REFLECTOR(inner))
1006 wrappedNative = XPCWrappedNative::Get(inner);
1007 if (wrappedNative) {
1008 iface = wrappedNative->GetIdentityObject();
1009 return NS_SUCCEEDED(iface->QueryInterface(*iid, dest));
1010 }
1011 // else...
1012
1013 // Deal with slim wrappers here.
1014 if (GetISupportsFromJSObject(inner ? inner : src, &iface)) {
1015 if (iface)
1016 return NS_SUCCEEDED(iface->QueryInterface(*iid, dest));
1017
1018 return false;
1019 }
1020 }
1021
1022 // else...
1023
1024 nsXPCWrappedJS* wrapper;
1025 nsresult rv = nsXPCWrappedJS::GetNewOrUsed(src, *iid, &wrapper);
1026 if (pErr)
1027 *pErr = rv;
1028 if (NS_SUCCEEDED(rv) && wrapper) {
1029 // If the caller wanted to aggregate this JS object to a native,
1030 // attach it to the wrapper. Note that we allow a maximum of one
1031 // aggregated native for a given XPCWrappedJS.
1032 if (aOuter)
1033 wrapper->SetAggregatedNativeObject(aOuter);
1034
1035 // We need to go through the QueryInterface logic to make this return
1036 // the right thing for the various 'special' interfaces; e.g.
1037 // nsIPropertyBag. We must use AggregatedQueryInterface in cases where
1038 // there is an outer to avoid nasty recursion.
1039 rv = aOuter ? wrapper->AggregatedQueryInterface(*iid, dest) :
1040 wrapper->QueryInterface(*iid, dest);
1041 if (pErr)
1042 *pErr = rv;
1043 NS_RELEASE(wrapper);
1044 return NS_SUCCEEDED(rv);
1045 }
1046
1047 // else...
1048 return false;
1049 }
1050
1051 /***************************************************************************/
1052 /***************************************************************************/
1053
1054 // static
1055 nsresult
1056 XPCConvert::ConstructException(nsresult rv, const char* message,
1057 const char* ifaceName, const char* methodName,
1058 nsISupports* data,
1059 nsIException** exceptn,
1060 JSContext* cx,
1061 jsval* jsExceptionPtr)
1062 {
1063 MOZ_ASSERT(!cx == !jsExceptionPtr, "Expected cx and jsExceptionPtr to cooccur.");
1064
1065 static const char format[] = "\'%s\' when calling method: [%s::%s]";
1066 const char * msg = message;
1067 nsXPIDLString xmsg;
1068 nsAutoCString sxmsg;
1069
1070 nsCOMPtr<nsIScriptError> errorObject = do_QueryInterface(data);
1071 if (errorObject) {
1072 if (NS_SUCCEEDED(errorObject->GetMessageMoz(getter_Copies(xmsg)))) {
1073 CopyUTF16toUTF8(xmsg, sxmsg);
1074 msg = sxmsg.get();
1075 }
1076 }
1077 if (!msg)
1078 if (!nsXPCException::NameAndFormatForNSResult(rv, nullptr, &msg) || ! msg)
1079 msg = "<error>";
1080
1081 nsCString msgStr(msg);
1082 if (ifaceName && methodName)
1083 msgStr.AppendPrintf(format, msg, ifaceName, methodName);
1084
1085 nsRefPtr<Exception> e = new Exception(msgStr, rv, EmptyCString(), nullptr, data);
1086
1087 if (cx && jsExceptionPtr) {
1088 e->StowJSVal(*jsExceptionPtr);
1089 }
1090
1091 e.forget(exceptn);
1092 return NS_OK;
1093 }
1094
1095 /********************************/
1096
1097 class MOZ_STACK_CLASS AutoExceptionRestorer
1098 {
1099 public:
1100 AutoExceptionRestorer(JSContext *cx, Value v)
1101 : mContext(cx), tvr(cx, v)
1102 {
1103 JS_ClearPendingException(mContext);
1104 }
1105
1106 ~AutoExceptionRestorer()
1107 {
1108 JS_SetPendingException(mContext, tvr);
1109 }
1110
1111 private:
1112 JSContext * const mContext;
1113 RootedValue tvr;
1114 };
1115
1116 // static
1117 nsresult
1118 XPCConvert::JSValToXPCException(MutableHandleValue s,
1119 const char* ifaceName,
1120 const char* methodName,
1121 nsIException** exceptn)
1122 {
1123 AutoJSContext cx;
1124 AutoExceptionRestorer aer(cx, s);
1125
1126 if (!JSVAL_IS_PRIMITIVE(s)) {
1127 // we have a JSObject
1128 RootedObject obj(cx, JSVAL_TO_OBJECT(s));
1129
1130 if (!obj) {
1131 NS_ERROR("when is an object not an object?");
1132 return NS_ERROR_FAILURE;
1133 }
1134
1135 // is this really a native xpcom object with a wrapper?
1136 JSObject *unwrapped = js::CheckedUnwrap(obj, /* stopAtOuter = */ false);
1137 if (!unwrapped)
1138 return NS_ERROR_XPC_SECURITY_MANAGER_VETO;
1139 XPCWrappedNative* wrapper = IS_WN_REFLECTOR(unwrapped) ? XPCWrappedNative::Get(unwrapped)
1140 : nullptr;
1141 if (wrapper) {
1142 nsISupports* supports = wrapper->GetIdentityObject();
1143 nsCOMPtr<nsIException> iface = do_QueryInterface(supports);
1144 if (iface) {
1145 // just pass through the exception (with extra ref and all)
1146 nsCOMPtr<nsIException> temp = iface;
1147 temp.forget(exceptn);
1148 return NS_OK;
1149 } else {
1150 // it is a wrapped native, but not an exception!
1151 return ConstructException(NS_ERROR_XPC_JS_THREW_NATIVE_OBJECT,
1152 nullptr, ifaceName, methodName, supports,
1153 exceptn, nullptr, nullptr);
1154 }
1155 } else {
1156 // It is a JSObject, but not a wrapped native...
1157
1158 // If it is an engine Error with an error report then let's
1159 // extract the report and build an xpcexception from that
1160 const JSErrorReport* report;
1161 if (nullptr != (report = JS_ErrorFromException(cx, obj))) {
1162 JSAutoByteString message;
1163 JSString* str;
1164 if (nullptr != (str = ToString(cx, s)))
1165 message.encodeLatin1(cx, str);
1166 return JSErrorToXPCException(message.ptr(), ifaceName,
1167 methodName, report, exceptn);
1168 }
1169
1170
1171 bool found;
1172
1173 // heuristic to see if it might be usable as an xpcexception
1174 if (!JS_HasProperty(cx, obj, "message", &found))
1175 return NS_ERROR_FAILURE;
1176
1177 if (found && !JS_HasProperty(cx, obj, "result", &found))
1178 return NS_ERROR_FAILURE;
1179
1180 if (found) {
1181 // lets try to build a wrapper around the JSObject
1182 nsXPCWrappedJS* jswrapper;
1183 nsresult rv =
1184 nsXPCWrappedJS::GetNewOrUsed(obj, NS_GET_IID(nsIException), &jswrapper);
1185 if (NS_FAILED(rv))
1186 return rv;
1187
1188 *exceptn = static_cast<nsIException *>(jswrapper->GetXPTCStub());
1189 return NS_OK;
1190 }
1191
1192
1193 // XXX we should do a check against 'js_ErrorClass' here and
1194 // do the right thing - even though it has no JSErrorReport,
1195 // The fact that it is a JSError exceptions means we can extract
1196 // particular info and our 'result' should reflect that.
1197
1198 // otherwise we'll just try to convert it to a string
1199
1200 JSString* str = ToString(cx, s);
1201 if (!str)
1202 return NS_ERROR_FAILURE;
1203
1204 JSAutoByteString strBytes(cx, str);
1205 if (!strBytes)
1206 return NS_ERROR_FAILURE;
1207
1208 return ConstructException(NS_ERROR_XPC_JS_THREW_JS_OBJECT,
1209 strBytes.ptr(), ifaceName, methodName,
1210 nullptr, exceptn, cx, s.address());
1211 }
1212 }
1213
1214 if (JSVAL_IS_VOID(s) || JSVAL_IS_NULL(s)) {
1215 return ConstructException(NS_ERROR_XPC_JS_THREW_NULL,
1216 nullptr, ifaceName, methodName, nullptr,
1217 exceptn, cx, s.address());
1218 }
1219
1220 if (JSVAL_IS_NUMBER(s)) {
1221 // lets see if it looks like an nsresult
1222 nsresult rv;
1223 double number;
1224 bool isResult = false;
1225
1226 if (JSVAL_IS_INT(s)) {
1227 rv = (nsresult) JSVAL_TO_INT(s);
1228 if (NS_FAILED(rv))
1229 isResult = true;
1230 else
1231 number = (double) JSVAL_TO_INT(s);
1232 } else {
1233 number = JSVAL_TO_DOUBLE(s);
1234 if (number > 0.0 &&
1235 number < (double)0xffffffff &&
1236 0.0 == fmod(number,1)) {
1237 // Visual Studio 9 doesn't allow casting directly from a
1238 // double to an enumeration type, contrary to 5.2.9(10) of
1239 // C++11, so add an intermediate cast.
1240 rv = (nsresult)(uint32_t) number;
1241 if (NS_FAILED(rv))
1242 isResult = true;
1243 }
1244 }
1245
1246 if (isResult)
1247 return ConstructException(rv, nullptr, ifaceName, methodName,
1248 nullptr, exceptn, cx, s.address());
1249 else {
1250 // XXX all this nsISupportsDouble code seems a little redundant
1251 // now that we're storing the jsval in the exception...
1252 nsISupportsDouble* data;
1253 nsCOMPtr<nsIComponentManager> cm;
1254 if (NS_FAILED(NS_GetComponentManager(getter_AddRefs(cm))) || !cm ||
1255 NS_FAILED(cm->CreateInstanceByContractID(NS_SUPPORTS_DOUBLE_CONTRACTID,
1256 nullptr,
1257 NS_GET_IID(nsISupportsDouble),
1258 (void**)&data)))
1259 return NS_ERROR_FAILURE;
1260 data->SetData(number);
1261 rv = ConstructException(NS_ERROR_XPC_JS_THREW_NUMBER, nullptr,
1262 ifaceName, methodName, data, exceptn, cx, s.address());
1263 NS_RELEASE(data);
1264 return rv;
1265 }
1266 }
1267
1268 // otherwise we'll just try to convert it to a string
1269 // Note: e.g., bools get converted to JSStrings by this code.
1270
1271 JSString* str = ToString(cx, s);
1272 if (str) {
1273 JSAutoByteString strBytes(cx, str);
1274 if (!!strBytes) {
1275 return ConstructException(NS_ERROR_XPC_JS_THREW_STRING,
1276 strBytes.ptr(), ifaceName, methodName,
1277 nullptr, exceptn, cx, s.address());
1278 }
1279 }
1280 return NS_ERROR_FAILURE;
1281 }
1282
1283 /********************************/
1284
1285 // static
1286 nsresult
1287 XPCConvert::JSErrorToXPCException(const char* message,
1288 const char* ifaceName,
1289 const char* methodName,
1290 const JSErrorReport* report,
1291 nsIException** exceptn)
1292 {
1293 AutoJSContext cx;
1294 nsresult rv = NS_ERROR_FAILURE;
1295 nsRefPtr<nsScriptError> data;
1296 if (report) {
1297 nsAutoString bestMessage;
1298 if (report && report->ucmessage) {
1299 bestMessage = static_cast<const char16_t*>(report->ucmessage);
1300 } else if (message) {
1301 CopyASCIItoUTF16(message, bestMessage);
1302 } else {
1303 bestMessage.AssignLiteral("JavaScript Error");
1304 }
1305
1306 const char16_t* uclinebuf =
1307 static_cast<const char16_t*>(report->uclinebuf);
1308
1309 data = new nsScriptError();
1310 data->InitWithWindowID(
1311 bestMessage,
1312 NS_ConvertASCIItoUTF16(report->filename),
1313 uclinebuf ? nsDependentString(uclinebuf) : EmptyString(),
1314 report->lineno,
1315 report->uctokenptr - report->uclinebuf, report->flags,
1316 NS_LITERAL_CSTRING("XPConnect JavaScript"),
1317 nsJSUtils::GetCurrentlyRunningCodeInnerWindowID(cx));
1318 }
1319
1320 if (data) {
1321 nsAutoCString formattedMsg;
1322 data->ToString(formattedMsg);
1323
1324 rv = ConstructException(NS_ERROR_XPC_JAVASCRIPT_ERROR_WITH_DETAILS,
1325 formattedMsg.get(), ifaceName, methodName,
1326 static_cast<nsIScriptError*>(data.get()),
1327 exceptn, nullptr, nullptr);
1328 } else {
1329 rv = ConstructException(NS_ERROR_XPC_JAVASCRIPT_ERROR,
1330 nullptr, ifaceName, methodName, nullptr,
1331 exceptn, nullptr, nullptr);
1332 }
1333 return rv;
1334 }
1335
1336 /***************************************************************************/
1337
1338 // array fun...
1339
1340 #ifdef POPULATE
1341 #undef POPULATE
1342 #endif
1343
1344 // static
1345 bool
1346 XPCConvert::NativeArray2JS(MutableHandleValue d, const void** s,
1347 const nsXPTType& type, const nsID* iid,
1348 uint32_t count, nsresult* pErr)
1349 {
1350 NS_PRECONDITION(s, "bad param");
1351
1352 AutoJSContext cx;
1353
1354 // XXX add support for putting chars in a string rather than an array
1355
1356 // XXX add support to indicate *which* array element was not convertable
1357
1358 RootedObject array(cx, JS_NewArrayObject(cx, count));
1359 if (!array)
1360 return false;
1361
1362 if (pErr)
1363 *pErr = NS_ERROR_XPC_BAD_CONVERT_NATIVE;
1364
1365 uint32_t i;
1366 RootedValue current(cx, JSVAL_NULL);
1367
1368 #define POPULATE(_t) \
1369 PR_BEGIN_MACRO \
1370 for (i = 0; i < count; i++) { \
1371 if (!NativeData2JS(&current, ((_t*)*s)+i, type, iid, pErr) || \
1372 !JS_SetElement(cx, array, i, current)) \
1373 goto failure; \
1374 } \
1375 PR_END_MACRO
1376
1377 // XXX check IsPtr - esp. to handle array of nsID (as opposed to nsID*)
1378
1379 switch (type.TagPart()) {
1380 case nsXPTType::T_I8 : POPULATE(int8_t); break;
1381 case nsXPTType::T_I16 : POPULATE(int16_t); break;
1382 case nsXPTType::T_I32 : POPULATE(int32_t); break;
1383 case nsXPTType::T_I64 : POPULATE(int64_t); break;
1384 case nsXPTType::T_U8 : POPULATE(uint8_t); break;
1385 case nsXPTType::T_U16 : POPULATE(uint16_t); break;
1386 case nsXPTType::T_U32 : POPULATE(uint32_t); break;
1387 case nsXPTType::T_U64 : POPULATE(uint64_t); break;
1388 case nsXPTType::T_FLOAT : POPULATE(float); break;
1389 case nsXPTType::T_DOUBLE : POPULATE(double); break;
1390 case nsXPTType::T_BOOL : POPULATE(bool); break;
1391 case nsXPTType::T_CHAR : POPULATE(char); break;
1392 case nsXPTType::T_WCHAR : POPULATE(jschar); break;
1393 case nsXPTType::T_VOID : NS_ERROR("bad type"); goto failure;
1394 case nsXPTType::T_IID : POPULATE(nsID*); break;
1395 case nsXPTType::T_DOMSTRING : NS_ERROR("bad type"); goto failure;
1396 case nsXPTType::T_CHAR_STR : POPULATE(char*); break;
1397 case nsXPTType::T_WCHAR_STR : POPULATE(jschar*); break;
1398 case nsXPTType::T_INTERFACE : POPULATE(nsISupports*); break;
1399 case nsXPTType::T_INTERFACE_IS : POPULATE(nsISupports*); break;
1400 case nsXPTType::T_UTF8STRING : NS_ERROR("bad type"); goto failure;
1401 case nsXPTType::T_CSTRING : NS_ERROR("bad type"); goto failure;
1402 case nsXPTType::T_ASTRING : NS_ERROR("bad type"); goto failure;
1403 default : NS_ERROR("bad type"); goto failure;
1404 }
1405
1406 if (pErr)
1407 *pErr = NS_OK;
1408 d.setObject(*array);
1409 return true;
1410
1411 failure:
1412 return false;
1413
1414 #undef POPULATE
1415 }
1416
1417
1418
1419 // Check that the tag part of the type matches the type
1420 // of the array. If the check succeeds, check that the size
1421 // of the output does not exceed UINT32_MAX bytes. Allocate
1422 // the memory and copy the elements by memcpy.
1423 static bool
1424 CheckTargetAndPopulate(const nsXPTType& type,
1425 uint8_t requiredType,
1426 size_t typeSize,
1427 uint32_t count,
1428 JSObject* tArr,
1429 void** output,
1430 nsresult* pErr)
1431 {
1432 // Check that the element type expected by the interface matches
1433 // the type of the elements in the typed array exactly, including
1434 // signedness.
1435 if (type.TagPart() != requiredType) {
1436 if (pErr)
1437 *pErr = NS_ERROR_XPC_BAD_CONVERT_JS;
1438
1439 return false;
1440 }
1441
1442 // Calulate the maximum number of elements that can fit in
1443 // UINT32_MAX bytes.
1444 size_t max = UINT32_MAX / typeSize;
1445
1446 // This could overflow on 32-bit systems so check max first.
1447 size_t byteSize = count * typeSize;
1448 if (count > max || !(*output = nsMemory::Alloc(byteSize))) {
1449 if (pErr)
1450 *pErr = NS_ERROR_OUT_OF_MEMORY;
1451
1452 return false;
1453 }
1454
1455 memcpy(*output, JS_GetArrayBufferViewData(tArr), byteSize);
1456 return true;
1457 }
1458
1459 // Fast conversion of typed arrays to native using memcpy.
1460 // No float or double canonicalization is done. Called by
1461 // JSarray2Native whenever a TypedArray is met. ArrayBuffers
1462 // are not accepted; create a properly typed array view on them
1463 // first. The element type of array must match the XPCOM
1464 // type in size, type and signedness exactly. As an exception,
1465 // Uint8ClampedArray is allowed for arrays of uint8_t. DataViews
1466 // are not supported.
1467
1468 // static
1469 bool
1470 XPCConvert::JSTypedArray2Native(void** d,
1471 JSObject* jsArray,
1472 uint32_t count,
1473 const nsXPTType& type,
1474 nsresult* pErr)
1475 {
1476 MOZ_ASSERT(jsArray, "bad param");
1477 MOZ_ASSERT(d, "bad param");
1478 MOZ_ASSERT(JS_IsTypedArrayObject(jsArray), "not a typed array");
1479
1480 // Check the actual length of the input array against the
1481 // given size_is.
1482 uint32_t len = JS_GetTypedArrayLength(jsArray);
1483 if (len < count) {
1484 if (pErr)
1485 *pErr = NS_ERROR_XPC_NOT_ENOUGH_ELEMENTS_IN_ARRAY;
1486
1487 return false;
1488 }
1489
1490 void* output = nullptr;
1491
1492 switch (JS_GetArrayBufferViewType(jsArray)) {
1493 case js::ArrayBufferView::TYPE_INT8:
1494 if (!CheckTargetAndPopulate(nsXPTType::T_I8, type,
1495 sizeof(int8_t), count,
1496 jsArray, &output, pErr)) {
1497 return false;
1498 }
1499 break;
1500
1501 case js::ArrayBufferView::TYPE_UINT8:
1502 case js::ArrayBufferView::TYPE_UINT8_CLAMPED:
1503 if (!CheckTargetAndPopulate(nsXPTType::T_U8, type,
1504 sizeof(uint8_t), count,
1505 jsArray, &output, pErr)) {
1506 return false;
1507 }
1508 break;
1509
1510 case js::ArrayBufferView::TYPE_INT16:
1511 if (!CheckTargetAndPopulate(nsXPTType::T_I16, type,
1512 sizeof(int16_t), count,
1513 jsArray, &output, pErr)) {
1514 return false;
1515 }
1516 break;
1517
1518 case js::ArrayBufferView::TYPE_UINT16:
1519 if (!CheckTargetAndPopulate(nsXPTType::T_U16, type,
1520 sizeof(uint16_t), count,
1521 jsArray, &output, pErr)) {
1522 return false;
1523 }
1524 break;
1525
1526 case js::ArrayBufferView::TYPE_INT32:
1527 if (!CheckTargetAndPopulate(nsXPTType::T_I32, type,
1528 sizeof(int32_t), count,
1529 jsArray, &output, pErr)) {
1530 return false;
1531 }
1532 break;
1533
1534 case js::ArrayBufferView::TYPE_UINT32:
1535 if (!CheckTargetAndPopulate(nsXPTType::T_U32, type,
1536 sizeof(uint32_t), count,
1537 jsArray, &output, pErr)) {
1538 return false;
1539 }
1540 break;
1541
1542 case js::ArrayBufferView::TYPE_FLOAT32:
1543 if (!CheckTargetAndPopulate(nsXPTType::T_FLOAT, type,
1544 sizeof(float), count,
1545 jsArray, &output, pErr)) {
1546 return false;
1547 }
1548 break;
1549
1550 case js::ArrayBufferView::TYPE_FLOAT64:
1551 if (!CheckTargetAndPopulate(nsXPTType::T_DOUBLE, type,
1552 sizeof(double), count,
1553 jsArray, &output, pErr)) {
1554 return false;
1555 }
1556 break;
1557
1558 // Yet another array type was defined? It is not supported yet...
1559 default:
1560 if (pErr)
1561 *pErr = NS_ERROR_XPC_BAD_CONVERT_JS;
1562
1563 return false;
1564 }
1565
1566 *d = output;
1567 if (pErr)
1568 *pErr = NS_OK;
1569
1570 return true;
1571 }
1572
1573 // static
1574 bool
1575 XPCConvert::JSArray2Native(void** d, HandleValue s,
1576 uint32_t count, const nsXPTType& type,
1577 const nsID* iid, nsresult* pErr)
1578 {
1579 MOZ_ASSERT(d, "bad param");
1580
1581 AutoJSContext cx;
1582
1583 // XXX add support for getting chars from strings
1584
1585 // XXX add support to indicate *which* array element was not convertable
1586
1587 if (s.isNullOrUndefined()) {
1588 if (0 != count) {
1589 if (pErr)
1590 *pErr = NS_ERROR_XPC_NOT_ENOUGH_ELEMENTS_IN_ARRAY;
1591 return false;
1592 }
1593
1594 *d = nullptr;
1595 return true;
1596 }
1597
1598 if (!s.isObject()) {
1599 if (pErr)
1600 *pErr = NS_ERROR_XPC_CANT_CONVERT_PRIMITIVE_TO_ARRAY;
1601 return false;
1602 }
1603
1604 RootedObject jsarray(cx, &s.toObject());
1605
1606 // If this is a typed array, then try a fast conversion with memcpy.
1607 if (JS_IsTypedArrayObject(jsarray)) {
1608 return JSTypedArray2Native(d, jsarray, count, type, pErr);
1609 }
1610
1611 if (!JS_IsArrayObject(cx, jsarray)) {
1612 if (pErr)
1613 *pErr = NS_ERROR_XPC_CANT_CONVERT_OBJECT_TO_ARRAY;
1614 return false;
1615 }
1616
1617 uint32_t len;
1618 if (!JS_GetArrayLength(cx, jsarray, &len) || len < count) {
1619 if (pErr)
1620 *pErr = NS_ERROR_XPC_NOT_ENOUGH_ELEMENTS_IN_ARRAY;
1621 return false;
1622 }
1623
1624 if (pErr)
1625 *pErr = NS_ERROR_XPC_BAD_CONVERT_JS;
1626
1627 #define POPULATE(_mode, _t) \
1628 PR_BEGIN_MACRO \
1629 cleanupMode = _mode; \
1630 size_t max = UINT32_MAX / sizeof(_t); \
1631 if (count > max || \
1632 nullptr == (array = nsMemory::Alloc(count * sizeof(_t)))) { \
1633 if (pErr) \
1634 *pErr = NS_ERROR_OUT_OF_MEMORY; \
1635 goto failure; \
1636 } \
1637 for (initedCount = 0; initedCount < count; initedCount++) { \
1638 if (!JS_GetElement(cx, jsarray, initedCount, &current) || \
1639 !JSData2Native(((_t*)array)+initedCount, current, type, \
1640 true, iid, pErr)) \
1641 goto failure; \
1642 } \
1643 PR_END_MACRO
1644
1645 // No Action, FRee memory, RElease object
1646 enum CleanupMode {na, fr, re};
1647
1648 CleanupMode cleanupMode;
1649
1650 void *array = nullptr;
1651 uint32_t initedCount;
1652 RootedValue current(cx);
1653
1654 // XXX check IsPtr - esp. to handle array of nsID (as opposed to nsID*)
1655 // XXX make extra space at end of char* and wchar* and null termintate
1656
1657 switch (type.TagPart()) {
1658 case nsXPTType::T_I8 : POPULATE(na, int8_t); break;
1659 case nsXPTType::T_I16 : POPULATE(na, int16_t); break;
1660 case nsXPTType::T_I32 : POPULATE(na, int32_t); break;
1661 case nsXPTType::T_I64 : POPULATE(na, int64_t); break;
1662 case nsXPTType::T_U8 : POPULATE(na, uint8_t); break;
1663 case nsXPTType::T_U16 : POPULATE(na, uint16_t); break;
1664 case nsXPTType::T_U32 : POPULATE(na, uint32_t); break;
1665 case nsXPTType::T_U64 : POPULATE(na, uint64_t); break;
1666 case nsXPTType::T_FLOAT : POPULATE(na, float); break;
1667 case nsXPTType::T_DOUBLE : POPULATE(na, double); break;
1668 case nsXPTType::T_BOOL : POPULATE(na, bool); break;
1669 case nsXPTType::T_CHAR : POPULATE(na, char); break;
1670 case nsXPTType::T_WCHAR : POPULATE(na, jschar); break;
1671 case nsXPTType::T_VOID : NS_ERROR("bad type"); goto failure;
1672 case nsXPTType::T_IID : POPULATE(fr, nsID*); break;
1673 case nsXPTType::T_DOMSTRING : NS_ERROR("bad type"); goto failure;
1674 case nsXPTType::T_CHAR_STR : POPULATE(fr, char*); break;
1675 case nsXPTType::T_WCHAR_STR : POPULATE(fr, jschar*); break;
1676 case nsXPTType::T_INTERFACE : POPULATE(re, nsISupports*); break;
1677 case nsXPTType::T_INTERFACE_IS : POPULATE(re, nsISupports*); break;
1678 case nsXPTType::T_UTF8STRING : NS_ERROR("bad type"); goto failure;
1679 case nsXPTType::T_CSTRING : NS_ERROR("bad type"); goto failure;
1680 case nsXPTType::T_ASTRING : NS_ERROR("bad type"); goto failure;
1681 default : NS_ERROR("bad type"); goto failure;
1682 }
1683
1684 *d = array;
1685 if (pErr)
1686 *pErr = NS_OK;
1687 return true;
1688
1689 failure:
1690 // we may need to cleanup the partially filled array of converted stuff
1691 if (array) {
1692 if (cleanupMode == re) {
1693 nsISupports** a = (nsISupports**) array;
1694 for (uint32_t i = 0; i < initedCount; i++) {
1695 nsISupports* p = a[i];
1696 NS_IF_RELEASE(p);
1697 }
1698 } else if (cleanupMode == fr) {
1699 void** a = (void**) array;
1700 for (uint32_t i = 0; i < initedCount; i++) {
1701 void* p = a[i];
1702 if (p) nsMemory::Free(p);
1703 }
1704 }
1705 nsMemory::Free(array);
1706 }
1707
1708 return false;
1709
1710 #undef POPULATE
1711 }
1712
1713 // static
1714 bool
1715 XPCConvert::NativeStringWithSize2JS(MutableHandleValue d, const void* s,
1716 const nsXPTType& type,
1717 uint32_t count,
1718 nsresult* pErr)
1719 {
1720 NS_PRECONDITION(s, "bad param");
1721
1722 AutoJSContext cx;
1723 if (pErr)
1724 *pErr = NS_ERROR_XPC_BAD_CONVERT_NATIVE;
1725
1726 switch (type.TagPart()) {
1727 case nsXPTType::T_PSTRING_SIZE_IS:
1728 {
1729 char* p = *((char**)s);
1730 if (!p)
1731 break;
1732 JSString* str;
1733 if (!(str = JS_NewStringCopyN(cx, p, count)))
1734 return false;
1735 d.setString(str);
1736 break;
1737 }
1738 case nsXPTType::T_PWSTRING_SIZE_IS:
1739 {
1740 jschar* p = *((jschar**)s);
1741 if (!p)
1742 break;
1743 JSString* str;
1744 if (!(str = JS_NewUCStringCopyN(cx, p, count)))
1745 return false;
1746 d.setString(str);
1747 break;
1748 }
1749 default:
1750 XPC_LOG_ERROR(("XPCConvert::NativeStringWithSize2JS : unsupported type"));
1751 return false;
1752 }
1753 return true;
1754 }
1755
1756 // static
1757 bool
1758 XPCConvert::JSStringWithSize2Native(void* d, HandleValue s,
1759 uint32_t count, const nsXPTType& type,
1760 nsresult* pErr)
1761 {
1762 NS_PRECONDITION(!JSVAL_IS_NULL(s), "bad param");
1763 NS_PRECONDITION(d, "bad param");
1764
1765 AutoJSContext cx;
1766 uint32_t len;
1767
1768 if (pErr)
1769 *pErr = NS_ERROR_XPC_BAD_CONVERT_NATIVE;
1770
1771 switch (type.TagPart()) {
1772 case nsXPTType::T_PSTRING_SIZE_IS:
1773 {
1774 if (JSVAL_IS_VOID(s) || JSVAL_IS_NULL(s)) {
1775 if (0 != count) {
1776 if (pErr)
1777 *pErr = NS_ERROR_XPC_NOT_ENOUGH_CHARS_IN_STRING;
1778 return false;
1779 }
1780 if (0 != count) {
1781 len = (count + 1) * sizeof(char);
1782 if (!(*((void**)d) = nsMemory::Alloc(len)))
1783 return false;
1784 return true;
1785 }
1786 // else ...
1787
1788 *((char**)d) = nullptr;
1789 return true;
1790 }
1791
1792 JSString* str = ToString(cx, s);
1793 if (!str) {
1794 return false;
1795 }
1796
1797 size_t length = JS_GetStringEncodingLength(cx, str);
1798 if (length == size_t(-1)) {
1799 return false;
1800 }
1801 if (length > count) {
1802 if (pErr)
1803 *pErr = NS_ERROR_XPC_NOT_ENOUGH_CHARS_IN_STRING;
1804 return false;
1805 }
1806 len = uint32_t(length);
1807
1808 if (len < count)
1809 len = count;
1810
1811 uint32_t alloc_len = (len + 1) * sizeof(char);
1812 char *buffer = static_cast<char *>(nsMemory::Alloc(alloc_len));
1813 if (!buffer) {
1814 return false;
1815 }
1816 JS_EncodeStringToBuffer(cx, str, buffer, len);
1817 buffer[len] = '\0';
1818 *((char**)d) = buffer;
1819
1820 return true;
1821 }
1822
1823 case nsXPTType::T_PWSTRING_SIZE_IS:
1824 {
1825 const jschar* chars=nullptr;
1826 JSString* str;
1827
1828 if (JSVAL_IS_VOID(s) || JSVAL_IS_NULL(s)) {
1829 if (0 != count) {
1830 if (pErr)
1831 *pErr = NS_ERROR_XPC_NOT_ENOUGH_CHARS_IN_STRING;
1832 return false;
1833 }
1834
1835 if (0 != count) {
1836 len = (count + 1) * sizeof(jschar);
1837 if (!(*((void**)d) = nsMemory::Alloc(len)))
1838 return false;
1839 return true;
1840 }
1841
1842 // else ...
1843 *((const jschar**)d) = nullptr;
1844 return true;
1845 }
1846
1847 if (!(str = ToString(cx, s))) {
1848 return false;
1849 }
1850
1851 len = JS_GetStringLength(str);
1852 if (len > count) {
1853 if (pErr)
1854 *pErr = NS_ERROR_XPC_NOT_ENOUGH_CHARS_IN_STRING;
1855 return false;
1856 }
1857 if (len < count)
1858 len = count;
1859
1860 if (!(chars = JS_GetStringCharsZ(cx, str))) {
1861 return false;
1862 }
1863 uint32_t alloc_len = (len + 1) * sizeof(jschar);
1864 if (!(*((void**)d) = nsMemory::Alloc(alloc_len))) {
1865 // XXX should report error
1866 return false;
1867 }
1868 memcpy(*((jschar**)d), chars, alloc_len);
1869 (*((jschar**)d))[count] = 0;
1870
1871 return true;
1872 }
1873 default:
1874 XPC_LOG_ERROR(("XPCConvert::JSStringWithSize2Native : unsupported type"));
1875 return false;
1876 }
1877 }
1878

mercurial