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