XPCConvert.cpp (48142B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */ 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/Range.h" 10 #include "mozilla/Sprintf.h" 11 12 #include "xpcprivate.h" 13 #include "nsIScriptError.h" 14 #include "nsISimpleEnumerator.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/Array.h" // JS::GetArrayLength, JS::IsArrayObject, JS::NewArrayObject 26 #include "js/CharacterEncoding.h" 27 #include "js/experimental/TypedData.h" // JS_GetArrayBufferViewType, JS_GetArrayBufferViewData, JS_GetTypedArrayLength, JS_IsTypedArrayObject 28 #include "js/MemoryFunctions.h" 29 #include "js/Object.h" // JS::GetClass 30 #include "js/PropertyAndElement.h" // JS_DefineElement, JS_GetElement 31 #include "js/String.h" // JS::StringHasLatin1Chars 32 33 #include "mozilla/dom/BindingUtils.h" 34 #include "mozilla/dom/DOMException.h" 35 #include "mozilla/dom/PrimitiveConversions.h" 36 #include "mozilla/dom/Promise.h" 37 38 using namespace xpc; 39 using namespace mozilla; 40 using namespace mozilla::dom; 41 using namespace JS; 42 43 // #define STRICT_CHECK_OF_UNICODE 44 #ifdef STRICT_CHECK_OF_UNICODE 45 # define ILLEGAL_RANGE(c) (0 != ((c) & 0xFF80)) 46 #else // STRICT_CHECK_OF_UNICODE 47 # define ILLEGAL_RANGE(c) (0 != ((c) & 0xFF00)) 48 #endif // STRICT_CHECK_OF_UNICODE 49 50 #define ILLEGAL_CHAR_RANGE(c) (0 != ((c) & 0x80)) 51 52 /***************************************************************************/ 53 54 // static 55 bool XPCConvert::GetISupportsFromJSObject(JSObject* obj, nsISupports** iface) { 56 if (JS::GetClass(obj)->slot0IsISupports()) { 57 *iface = JS::GetObjectISupports<nsISupports>(obj); 58 return true; 59 } 60 *iface = UnwrapDOMObjectToISupports(obj); 61 return !!*iface; 62 } 63 64 /***************************************************************************/ 65 66 // static 67 bool XPCConvert::NativeData2JS(JSContext* cx, MutableHandleValue d, 68 const void* s, const nsXPTType& type, 69 const nsID* iid, uint32_t arrlen, 70 nsresult* pErr) { 71 MOZ_ASSERT(s, "bad param"); 72 73 if (pErr) { 74 *pErr = NS_ERROR_XPC_BAD_CONVERT_NATIVE; 75 } 76 77 switch (type.Tag()) { 78 case nsXPTType::T_I8: 79 d.setInt32(*static_cast<const int8_t*>(s)); 80 return true; 81 case nsXPTType::T_I16: 82 d.setInt32(*static_cast<const int16_t*>(s)); 83 return true; 84 case nsXPTType::T_I32: 85 d.setInt32(*static_cast<const int32_t*>(s)); 86 return true; 87 case nsXPTType::T_I64: 88 d.setNumber(static_cast<double>(*static_cast<const int64_t*>(s))); 89 return true; 90 case nsXPTType::T_U8: 91 d.setInt32(*static_cast<const uint8_t*>(s)); 92 return true; 93 case nsXPTType::T_U16: 94 d.setInt32(*static_cast<const uint16_t*>(s)); 95 return true; 96 case nsXPTType::T_U32: 97 d.setNumber(*static_cast<const uint32_t*>(s)); 98 return true; 99 case nsXPTType::T_U64: 100 d.setNumber(static_cast<double>(*static_cast<const uint64_t*>(s))); 101 return true; 102 case nsXPTType::T_FLOAT: 103 d.setNumber(*static_cast<const float*>(s)); 104 return true; 105 case nsXPTType::T_DOUBLE: 106 d.set(JS_NumberValue(*static_cast<const double*>(s))); 107 return true; 108 case nsXPTType::T_BOOL: 109 d.setBoolean(*static_cast<const bool*>(s)); 110 return true; 111 case nsXPTType::T_CHAR: { 112 char p = *static_cast<const char*>(s); 113 114 #ifdef STRICT_CHECK_OF_UNICODE 115 MOZ_ASSERT(!ILLEGAL_CHAR_RANGE(p), "passing non ASCII data"); 116 #endif // STRICT_CHECK_OF_UNICODE 117 118 JSString* str = JS_NewStringCopyN(cx, &p, 1); 119 if (!str) { 120 return false; 121 } 122 123 d.setString(str); 124 return true; 125 } 126 case nsXPTType::T_WCHAR: { 127 char16_t p = *static_cast<const char16_t*>(s); 128 129 JSString* str = JS_NewUCStringCopyN(cx, &p, 1); 130 if (!str) { 131 return false; 132 } 133 134 d.setString(str); 135 return true; 136 } 137 138 case nsXPTType::T_JSVAL: { 139 d.set(*static_cast<const Value*>(s)); 140 return JS_WrapValue(cx, d); 141 } 142 143 case nsXPTType::T_VOID: 144 XPC_LOG_ERROR(("XPCConvert::NativeData2JS : void* params not supported")); 145 return false; 146 147 case nsXPTType::T_NSIDPTR: { 148 nsID* iid2 = *static_cast<nsID* const*>(s); 149 if (!iid2) { 150 d.setNull(); 151 return true; 152 } 153 154 return xpc::ID2JSValue(cx, *iid2, d); 155 } 156 157 case nsXPTType::T_NSID: 158 return xpc::ID2JSValue(cx, *static_cast<const nsID*>(s), d); 159 160 case nsXPTType::T_ASTRING: { 161 const nsAString* p = static_cast<const nsAString*>(s); 162 if (!p || p->IsVoid()) { 163 d.setNull(); 164 return true; 165 } 166 return NonVoidStringToJsval(cx, *p, d); 167 } 168 169 case nsXPTType::T_CHAR_STR: { 170 const char* p = *static_cast<const char* const*>(s); 171 arrlen = p ? strlen(p) : 0; 172 [[fallthrough]]; 173 } 174 case nsXPTType::T_PSTRING_SIZE_IS: { 175 const char* p = *static_cast<const char* const*>(s); 176 if (!p) { 177 d.setNull(); 178 return true; 179 } 180 181 #ifdef STRICT_CHECK_OF_UNICODE 182 bool isAscii = true; 183 for (uint32_t i = 0; i < arrlen; i++) { 184 if (ILLEGAL_CHAR_RANGE(p[i])) { 185 isAscii = false; 186 } 187 } 188 MOZ_ASSERT(isAscii, "passing non ASCII data"); 189 #endif // STRICT_CHECK_OF_UNICODE 190 191 JSString* str = JS_NewStringCopyN(cx, p, arrlen); 192 if (!str) { 193 return false; 194 } 195 196 d.setString(str); 197 return true; 198 } 199 200 case nsXPTType::T_WCHAR_STR: { 201 const char16_t* p = *static_cast<const char16_t* const*>(s); 202 arrlen = p ? nsCharTraits<char16_t>::length(p) : 0; 203 [[fallthrough]]; 204 } 205 case nsXPTType::T_PWSTRING_SIZE_IS: { 206 const char16_t* p = *static_cast<const char16_t* const*>(s); 207 if (!p) { 208 d.setNull(); 209 return true; 210 } 211 212 JSString* str = JS_NewUCStringCopyN(cx, p, arrlen); 213 if (!str) { 214 return false; 215 } 216 217 d.setString(str); 218 return true; 219 } 220 221 case nsXPTType::T_UTF8STRING: { 222 const nsACString* utf8String = static_cast<const nsACString*>(s); 223 224 if (!utf8String || utf8String->IsVoid()) { 225 d.setNull(); 226 return true; 227 } 228 229 if (utf8String->IsEmpty()) { 230 d.set(JS_GetEmptyStringValue(cx)); 231 return true; 232 } 233 234 uint32_t len = utf8String->Length(); 235 auto allocLen = CheckedUint32(len) + 1; 236 if (!allocLen.isValid()) { 237 return false; 238 } 239 240 // Usage of UTF-8 in XPConnect is mostly for things that are 241 // almost always ASCII, so the inexact allocations below 242 // should be fine. 243 244 // Is the string buffer is already valid latin1 (i.e. it is ASCII). 245 // 246 // NOTE: NonVoidUTF8StringToJsval cannot be used here because 247 // it requires valid UTF-8 sequence. 248 if (mozilla::IsAscii(*utf8String)) { 249 return NonVoidLatin1StringToJsval(cx, *utf8String, d); 250 } 251 252 // 1-byte sequences decode to 1 UTF-16 code unit 253 // 2-byte sequences decode to 1 UTF-16 code unit 254 // 3-byte sequences decode to 1 UTF-16 code unit 255 // 4-byte sequences decode to 2 UTF-16 code units 256 // So the number of output code units never exceeds 257 // the number of input code units (but see the comment 258 // below). allocLen already takes the zero terminator 259 // into account. 260 allocLen *= sizeof(char16_t); 261 if (!allocLen.isValid()) { 262 return false; 263 } 264 265 JS::UniqueTwoByteChars buffer( 266 static_cast<char16_t*>(JS_string_malloc(cx, allocLen.value()))); 267 if (!buffer) { 268 return false; 269 } 270 271 // For its internal simplicity, ConvertUTF8toUTF16 requires the 272 // destination to be one code unit longer than the source, but 273 // it never actually writes more code units than the number of 274 // code units in the source. That's why it's OK to claim the 275 // output buffer has len + 1 space but then still expect to 276 // have space for the zero terminator. 277 size_t written = 278 ConvertUtf8toUtf16(*utf8String, Span(buffer.get(), allocLen.value())); 279 MOZ_RELEASE_ASSERT(written <= len); 280 buffer[written] = 0; 281 282 JSString* str = JS_NewUCStringDontDeflate(cx, std::move(buffer), written); 283 if (!str) { 284 return false; 285 } 286 287 d.setString(str); 288 return true; 289 } 290 case nsXPTType::T_CSTRING: { 291 const nsACString* cString = static_cast<const nsACString*>(s); 292 293 if (!cString || cString->IsVoid()) { 294 d.setNull(); 295 return true; 296 } 297 298 // c-strings (binary blobs) are Latin1 string in JSAPI. 299 return NonVoidLatin1StringToJsval(cx, *cString, d); 300 } 301 302 case nsXPTType::T_INTERFACE: 303 case nsXPTType::T_INTERFACE_IS: { 304 nsISupports* iface = *static_cast<nsISupports* const*>(s); 305 if (!iface) { 306 d.setNull(); 307 return true; 308 } 309 310 if (iid->Equals(NS_GET_IID(nsIVariant))) { 311 nsCOMPtr<nsIVariant> variant = do_QueryInterface(iface); 312 if (!variant) { 313 return false; 314 } 315 316 return XPCVariant::VariantDataToJS(cx, variant, pErr, d); 317 } 318 319 xpcObjectHelper helper(iface); 320 return NativeInterface2JSObject(cx, d, helper, iid, true, pErr); 321 } 322 323 case nsXPTType::T_DOMOBJECT: { 324 void* ptr = *static_cast<void* const*>(s); 325 if (!ptr) { 326 d.setNull(); 327 return true; 328 } 329 330 return type.GetDOMObjectInfo().Wrap(cx, ptr, d); 331 } 332 333 case nsXPTType::T_PROMISE: { 334 Promise* promise = *static_cast<Promise* const*>(s); 335 if (!promise) { 336 d.setNull(); 337 return true; 338 } 339 340 RootedObject jsobj(cx, promise->PromiseObj()); 341 if (!JS_WrapObject(cx, &jsobj)) { 342 return false; 343 } 344 d.setObject(*jsobj); 345 return true; 346 } 347 348 case nsXPTType::T_LEGACY_ARRAY: 349 return NativeArray2JS(cx, d, *static_cast<const void* const*>(s), 350 type.ArrayElementType(), iid, arrlen, pErr); 351 352 case nsXPTType::T_ARRAY: { 353 auto* array = static_cast<const xpt::detail::UntypedTArray*>(s); 354 return NativeArray2JS(cx, d, array->Elements(), type.ArrayElementType(), 355 iid, array->Length(), pErr); 356 } 357 358 default: 359 NS_ERROR("bad type"); 360 return false; 361 } 362 } 363 364 /***************************************************************************/ 365 366 #ifdef DEBUG 367 static bool CheckChar16InCharRange(char16_t c) { 368 if (ILLEGAL_RANGE(c)) { 369 /* U+0080/U+0100 - U+FFFF data lost. */ 370 static const size_t MSG_BUF_SIZE = 64; 371 char msg[MSG_BUF_SIZE]; 372 SprintfLiteral(msg, 373 "char16_t out of char range; high bits of data lost: 0x%x", 374 int(c)); 375 NS_WARNING(msg); 376 return false; 377 } 378 379 return true; 380 } 381 382 template <typename CharT> 383 static void CheckCharsInCharRange(const CharT* chars, size_t len) { 384 for (size_t i = 0; i < len; i++) { 385 if (!CheckChar16InCharRange(chars[i])) { 386 break; 387 } 388 } 389 } 390 #endif 391 392 template <typename T> 393 bool ConvertToPrimitive(JSContext* cx, HandleValue v, T* retval) { 394 return ValueToPrimitive<T, eDefault>(cx, v, "Value", retval); 395 } 396 397 // static 398 bool XPCConvert::JSData2Native(JSContext* cx, void* d, HandleValue s, 399 const nsXPTType& type, const nsID* iid, 400 uint32_t arrlen, nsresult* pErr) { 401 MOZ_ASSERT(d, "bad param"); 402 403 js::AssertSameCompartment(cx, s); 404 405 if (pErr) { 406 *pErr = NS_ERROR_XPC_BAD_CONVERT_JS; 407 } 408 409 bool sizeis = 410 type.Tag() == TD_PSTRING_SIZE_IS || type.Tag() == TD_PWSTRING_SIZE_IS; 411 412 switch (type.Tag()) { 413 case nsXPTType::T_I8: 414 return ConvertToPrimitive(cx, s, static_cast<int8_t*>(d)); 415 case nsXPTType::T_I16: 416 return ConvertToPrimitive(cx, s, static_cast<int16_t*>(d)); 417 case nsXPTType::T_I32: 418 return ConvertToPrimitive(cx, s, static_cast<int32_t*>(d)); 419 case nsXPTType::T_I64: 420 return ConvertToPrimitive(cx, s, static_cast<int64_t*>(d)); 421 case nsXPTType::T_U8: 422 return ConvertToPrimitive(cx, s, static_cast<uint8_t*>(d)); 423 case nsXPTType::T_U16: 424 return ConvertToPrimitive(cx, s, static_cast<uint16_t*>(d)); 425 case nsXPTType::T_U32: 426 return ConvertToPrimitive(cx, s, static_cast<uint32_t*>(d)); 427 case nsXPTType::T_U64: 428 return ConvertToPrimitive(cx, s, static_cast<uint64_t*>(d)); 429 case nsXPTType::T_FLOAT: 430 return ConvertToPrimitive(cx, s, static_cast<float*>(d)); 431 case nsXPTType::T_DOUBLE: 432 return ConvertToPrimitive(cx, s, static_cast<double*>(d)); 433 case nsXPTType::T_BOOL: 434 return ConvertToPrimitive(cx, s, static_cast<bool*>(d)); 435 case nsXPTType::T_CHAR: { 436 JSString* str = ToString(cx, s); 437 if (!str) { 438 return false; 439 } 440 441 char16_t ch; 442 if (JS_GetStringLength(str) == 0) { 443 ch = 0; 444 } else { 445 if (!JS_GetStringCharAt(cx, str, 0, &ch)) { 446 return false; 447 } 448 } 449 #ifdef DEBUG 450 CheckChar16InCharRange(ch); 451 #endif 452 *((char*)d) = char(ch); 453 break; 454 } 455 case nsXPTType::T_WCHAR: { 456 JSString* str; 457 if (!(str = ToString(cx, s))) { 458 return false; 459 } 460 size_t length = JS_GetStringLength(str); 461 if (length == 0) { 462 *((uint16_t*)d) = 0; 463 break; 464 } 465 466 char16_t ch; 467 if (!JS_GetStringCharAt(cx, str, 0, &ch)) { 468 return false; 469 } 470 471 *((uint16_t*)d) = uint16_t(ch); 472 break; 473 } 474 case nsXPTType::T_JSVAL: 475 *((Value*)d) = s; 476 break; 477 case nsXPTType::T_VOID: 478 XPC_LOG_ERROR(("XPCConvert::JSData2Native : void* params not supported")); 479 NS_ERROR("void* params not supported"); 480 return false; 481 482 case nsXPTType::T_NSIDPTR: 483 if (Maybe<nsID> id = xpc::JSValue2ID(cx, s)) { 484 *((const nsID**)d) = id.ref().Clone(); 485 return true; 486 } 487 return false; 488 489 case nsXPTType::T_NSID: 490 if (Maybe<nsID> id = xpc::JSValue2ID(cx, s)) { 491 *((nsID*)d) = id.ref(); 492 return true; 493 } 494 return false; 495 496 case nsXPTType::T_ASTRING: { 497 nsAString* ws = (nsAString*)d; 498 if (s.isUndefined() || s.isNull()) { 499 ws->SetIsVoid(true); 500 return true; 501 } 502 size_t length = 0; 503 JSString* str = ToString(cx, s); 504 if (!str) { 505 return false; 506 } 507 508 length = JS_GetStringLength(str); 509 if (!length) { 510 ws->Truncate(); 511 return true; 512 } 513 514 return AssignJSString(cx, *ws, str); 515 } 516 517 case nsXPTType::T_CHAR_STR: 518 case nsXPTType::T_PSTRING_SIZE_IS: { 519 if (s.isUndefined() || s.isNull()) { 520 if (sizeis && 0 != arrlen) { 521 if (pErr) { 522 *pErr = NS_ERROR_XPC_NOT_ENOUGH_CHARS_IN_STRING; 523 } 524 return false; 525 } 526 *((char**)d) = nullptr; 527 return true; 528 } 529 530 JSString* str = ToString(cx, s); 531 if (!str) { 532 return false; 533 } 534 535 #ifdef DEBUG 536 if (JS::StringHasLatin1Chars(str)) { 537 size_t len; 538 AutoCheckCannotGC nogc; 539 const Latin1Char* chars = 540 JS_GetLatin1StringCharsAndLength(cx, nogc, str, &len); 541 if (chars) { 542 CheckCharsInCharRange(chars, len); 543 } 544 } else { 545 size_t len; 546 AutoCheckCannotGC nogc; 547 const char16_t* chars = 548 JS_GetTwoByteStringCharsAndLength(cx, nogc, str, &len); 549 if (chars) { 550 CheckCharsInCharRange(chars, len); 551 } 552 } 553 #endif // DEBUG 554 555 size_t length = JS_GetStringEncodingLength(cx, str); 556 if (length == size_t(-1)) { 557 return false; 558 } 559 if (sizeis) { 560 if (length > arrlen) { 561 if (pErr) { 562 *pErr = NS_ERROR_XPC_NOT_ENOUGH_CHARS_IN_STRING; 563 } 564 return false; 565 } 566 if (length < arrlen) { 567 length = arrlen; 568 } 569 } 570 char* buffer = static_cast<char*>(moz_xmalloc(length + 1)); 571 if (!JS_EncodeStringToBuffer(cx, str, buffer, length)) { 572 free(buffer); 573 return false; 574 } 575 buffer[length] = '\0'; 576 *((void**)d) = buffer; 577 return true; 578 } 579 580 case nsXPTType::T_WCHAR_STR: 581 case nsXPTType::T_PWSTRING_SIZE_IS: { 582 JSString* str; 583 584 if (s.isUndefined() || s.isNull()) { 585 if (sizeis && 0 != arrlen) { 586 if (pErr) { 587 *pErr = NS_ERROR_XPC_NOT_ENOUGH_CHARS_IN_STRING; 588 } 589 return false; 590 } 591 *((char16_t**)d) = nullptr; 592 return true; 593 } 594 595 if (!(str = ToString(cx, s))) { 596 return false; 597 } 598 size_t len = JS_GetStringLength(str); 599 if (sizeis) { 600 if (len > arrlen) { 601 if (pErr) { 602 *pErr = NS_ERROR_XPC_NOT_ENOUGH_CHARS_IN_STRING; 603 } 604 return false; 605 } 606 if (len < arrlen) { 607 len = arrlen; 608 } 609 } 610 611 size_t byte_len = (len + 1) * sizeof(char16_t); 612 *((void**)d) = moz_xmalloc(byte_len); 613 mozilla::Range<char16_t> destChars(*((char16_t**)d), len + 1); 614 if (!JS_CopyStringChars(cx, destChars, str)) { 615 return false; 616 } 617 destChars[len] = 0; 618 619 return true; 620 } 621 622 case nsXPTType::T_UTF8STRING: { 623 nsACString* rs = (nsACString*)d; 624 if (s.isNull() || s.isUndefined()) { 625 rs->SetIsVoid(true); 626 return true; 627 } 628 629 // The JS val is neither null nor void... 630 JSString* str = ToString(cx, s); 631 if (!str) { 632 return false; 633 } 634 635 size_t length = JS_GetStringLength(str); 636 if (!length) { 637 rs->Truncate(); 638 return true; 639 } 640 641 return AssignJSString(cx, *rs, str); 642 } 643 644 case nsXPTType::T_CSTRING: { 645 nsACString* rs = (nsACString*)d; 646 if (s.isNull() || s.isUndefined()) { 647 rs->SetIsVoid(true); 648 return true; 649 } 650 651 // The JS val is neither null nor void... 652 653 JSString* str; 654 size_t length; 655 if (s.isString()) { 656 str = s.toString(); 657 658 length = JS::GetStringLength(str); 659 if (!length) { 660 rs->Truncate(); 661 return true; 662 } 663 664 // The string can be an external latin-1 string created in 665 // XPCConvert::NativeData2JS's nsXPTType::T_CSTRING case. 666 if (XPCStringConvert::MaybeAssignLatin1StringChars(str, length, *rs)) { 667 return true; 668 } 669 } else { 670 str = ToString(cx, s); 671 if (!str) { 672 return false; 673 } 674 675 length = JS_GetStringEncodingLength(cx, str); 676 if (length == size_t(-1)) { 677 return false; 678 } 679 680 if (!length) { 681 rs->Truncate(); 682 return true; 683 } 684 } 685 686 if (!rs->SetLength(uint32_t(length), fallible)) { 687 if (pErr) { 688 *pErr = NS_ERROR_OUT_OF_MEMORY; 689 } 690 return false; 691 } 692 if (rs->Length() != uint32_t(length)) { 693 return false; 694 } 695 if (!JS_EncodeStringToBuffer(cx, str, rs->BeginWriting(), length)) { 696 return false; 697 } 698 699 return true; 700 } 701 702 case nsXPTType::T_INTERFACE: 703 case nsXPTType::T_INTERFACE_IS: { 704 MOZ_ASSERT(iid, "can't do interface conversions without iid"); 705 706 if (iid->Equals(NS_GET_IID(nsIVariant))) { 707 nsCOMPtr<nsIVariant> variant = XPCVariant::newVariant(cx, s); 708 if (!variant) { 709 return false; 710 } 711 712 variant.forget(static_cast<nsISupports**>(d)); 713 return true; 714 } 715 716 if (s.isNullOrUndefined()) { 717 *((nsISupports**)d) = nullptr; 718 return true; 719 } 720 721 // only wrap JSObjects 722 if (!s.isObject()) { 723 if (pErr && s.isInt32() && 0 == s.toInt32()) { 724 *pErr = NS_ERROR_XPC_BAD_CONVERT_JS_ZERO_ISNOT_NULL; 725 } 726 return false; 727 } 728 729 RootedObject src(cx, &s.toObject()); 730 return JSObject2NativeInterface(cx, (void**)d, src, iid, nullptr, pErr); 731 } 732 733 case nsXPTType::T_DOMOBJECT: { 734 if (s.isNullOrUndefined()) { 735 *((void**)d) = nullptr; 736 return true; 737 } 738 739 // Can't handle non-JSObjects 740 if (!s.isObject()) { 741 return false; 742 } 743 744 nsresult err = type.GetDOMObjectInfo().Unwrap(s, (void**)d, cx); 745 if (pErr) { 746 *pErr = err; 747 } 748 return NS_SUCCEEDED(err); 749 } 750 751 case nsXPTType::T_PROMISE: { 752 nsIGlobalObject* glob = CurrentNativeGlobal(cx); 753 if (!glob) { 754 if (pErr) { 755 *pErr = NS_ERROR_UNEXPECTED; 756 } 757 return false; 758 } 759 760 // Call Promise::Resolve to create a Promise object. This allows us to 761 // support returning non-promise values from Promise-returning functions 762 // in JS. 763 IgnoredErrorResult err; 764 *(Promise**)d = Promise::Resolve(glob, cx, s, err).take(); 765 bool ok = !err.Failed(); 766 if (pErr) { 767 *pErr = err.StealNSResult(); 768 } 769 770 return ok; 771 } 772 773 case nsXPTType::T_LEGACY_ARRAY: { 774 void** dest = (void**)d; 775 const nsXPTType& elty = type.ArrayElementType(); 776 777 *dest = nullptr; 778 779 // FIXME: XPConnect historically has shortcut the JSArray2Native codepath 780 // in its caller if arrlen is 0, allowing arbitrary values to be passed as 781 // arrays and interpreted as the empty array (bug 1458987). 782 // 783 // NOTE: Once this is fixed, null/undefined should be allowed for arrays 784 // if arrlen is 0. 785 if (arrlen == 0) { 786 return true; 787 } 788 789 bool ok = JSArray2Native( 790 cx, s, elty, iid, pErr, [&](uint32_t* aLength) -> void* { 791 // Check that we have enough elements in our array. 792 if (*aLength < arrlen) { 793 if (pErr) { 794 *pErr = NS_ERROR_XPC_NOT_ENOUGH_ELEMENTS_IN_ARRAY; 795 } 796 return nullptr; 797 } 798 *aLength = arrlen; 799 800 // Allocate the backing buffer & return it. 801 *dest = moz_xmalloc(*aLength * elty.Stride()); 802 return *dest; 803 }); 804 805 if (!ok && *dest) { 806 // An error occurred, free any allocated backing buffer. 807 free(*dest); 808 *dest = nullptr; 809 } 810 return ok; 811 } 812 813 case nsXPTType::T_ARRAY: { 814 auto* dest = (xpt::detail::UntypedTArray*)d; 815 const nsXPTType& elty = type.ArrayElementType(); 816 817 bool ok = JSArray2Native(cx, s, elty, iid, pErr, 818 [&](uint32_t* aLength) -> void* { 819 if (!dest->SetLength(elty, *aLength)) { 820 if (pErr) { 821 *pErr = NS_ERROR_OUT_OF_MEMORY; 822 } 823 return nullptr; 824 } 825 return dest->Elements(); 826 }); 827 828 if (!ok) { 829 // An error occurred, free any allocated backing buffer. 830 dest->Clear(); 831 } 832 return ok; 833 } 834 835 default: 836 NS_ERROR("bad type"); 837 return false; 838 } 839 return true; 840 } 841 842 /***************************************************************************/ 843 // static 844 bool XPCConvert::NativeInterface2JSObject(JSContext* cx, MutableHandleValue d, 845 xpcObjectHelper& aHelper, 846 const nsID* iid, 847 bool allowNativeWrapper, 848 nsresult* pErr) { 849 if (!iid) { 850 iid = &NS_GET_IID(nsISupports); 851 } 852 853 d.setNull(); 854 if (!aHelper.Object()) { 855 return true; 856 } 857 if (pErr) { 858 *pErr = NS_ERROR_XPC_BAD_CONVERT_NATIVE; 859 } 860 861 // We used to have code here that unwrapped and simply exposed the 862 // underlying JSObject. That caused anomolies when JSComponents were 863 // accessed from other JS code - they didn't act like other xpconnect 864 // wrapped components. So, instead, we create "double wrapped" objects 865 // (that means an XPCWrappedNative around an nsXPCWrappedJS). This isn't 866 // optimal -- we could detect this and roll the functionality into a 867 // single wrapper, but the current solution is good enough for now. 868 XPCWrappedNativeScope* xpcscope = ObjectScope(JS::CurrentGlobalOrNull(cx)); 869 if (!xpcscope) { 870 return false; 871 } 872 873 JSAutoRealm ar(cx, xpcscope->GetGlobalForWrappedNatives()); 874 875 // First, see if this object supports the wrapper cache. In that case, the 876 // object to use is found as cache->GetWrapper(). If that is null, then the 877 // object will create (and fill the cache) from its WrapObject call. 878 nsWrapperCache* cache = aHelper.GetWrapperCache(); 879 880 RootedObject flat(cx, cache ? cache->GetWrapper() : nullptr); 881 if (!flat && cache) { 882 RootedObject global(cx, CurrentGlobalOrNull(cx)); 883 flat = cache->WrapObject(cx, nullptr); 884 if (!flat) { 885 return false; 886 } 887 } 888 if (flat) { 889 if (allowNativeWrapper && !JS_WrapObject(cx, &flat)) { 890 return false; 891 } 892 d.setObjectOrNull(flat); 893 return true; 894 } 895 896 // Go ahead and create an XPCWrappedNative for this object. 897 RefPtr<XPCNativeInterface> iface = XPCNativeInterface::GetNewOrUsed(cx, iid); 898 if (!iface) { 899 return false; 900 } 901 902 RefPtr<XPCWrappedNative> wrapper; 903 nsresult rv = XPCWrappedNative::GetNewOrUsed(cx, aHelper, xpcscope, iface, 904 getter_AddRefs(wrapper)); 905 if (NS_FAILED(rv) && pErr) { 906 *pErr = rv; 907 } 908 909 // If creating the wrapped native failed, then return early. 910 if (NS_FAILED(rv) || !wrapper) { 911 return false; 912 } 913 914 // If we're not creating security wrappers, we can return the 915 // XPCWrappedNative as-is here. 916 flat = wrapper->GetFlatJSObject(); 917 if (!allowNativeWrapper) { 918 d.setObjectOrNull(flat); 919 if (pErr) { 920 *pErr = NS_OK; 921 } 922 return true; 923 } 924 925 // The call to wrap here handles both cross-compartment and same-compartment 926 // security wrappers. 927 RootedObject original(cx, flat); 928 if (!JS_WrapObject(cx, &flat)) { 929 return false; 930 } 931 932 d.setObjectOrNull(flat); 933 934 if (pErr) { 935 *pErr = NS_OK; 936 } 937 938 return true; 939 } 940 941 /***************************************************************************/ 942 943 // static 944 bool XPCConvert::JSObject2NativeInterface(JSContext* cx, void** dest, 945 HandleObject src, const nsID* iid, 946 nsISupports* aOuter, nsresult* pErr) { 947 MOZ_ASSERT(dest, "bad param"); 948 MOZ_ASSERT(src, "bad param"); 949 MOZ_ASSERT(iid, "bad param"); 950 951 js::AssertSameCompartment(cx, src); 952 953 *dest = nullptr; 954 if (pErr) { 955 *pErr = NS_ERROR_XPC_BAD_CONVERT_JS; 956 } 957 958 nsISupports* iface; 959 960 if (!aOuter) { 961 // Note that if we have a non-null aOuter then it means that we are 962 // forcing the creation of a wrapper even if the object *is* a 963 // wrappedNative or other wise has 'nsISupportness'. 964 // This allows wrapJSAggregatedToNative to work. 965 966 // If we're looking at a security wrapper, see now if we're allowed to 967 // pass it to C++. If we are, then fall through to the code below. If 968 // we aren't, throw an exception eagerly. 969 // 970 // NB: It's very important that we _don't_ unwrap in the aOuter case, 971 // because the caller may explicitly want to create the XPCWrappedJS 972 // around a security wrapper. XBL does this with Xrays from the XBL 973 // scope - see nsBindingManager::GetBindingImplementation. 974 // 975 // It's also very important that "inner" be rooted here. 976 RootedObject inner( 977 cx, js::CheckedUnwrapDynamic(src, cx, 978 /* stopAtWindowProxy = */ false)); 979 if (!inner) { 980 if (pErr) { 981 *pErr = NS_ERROR_XPC_SECURITY_MANAGER_VETO; 982 } 983 return false; 984 } 985 986 // Is this really a native xpcom object with a wrapper? 987 XPCWrappedNative* wrappedNative = nullptr; 988 if (IsWrappedNativeReflector(inner)) { 989 wrappedNative = XPCWrappedNative::Get(inner); 990 } 991 if (wrappedNative) { 992 iface = wrappedNative->GetIdentityObject(); 993 return NS_SUCCEEDED(iface->QueryInterface(*iid, dest)); 994 } 995 // else... 996 997 // Deal with slim wrappers here. 998 if (GetISupportsFromJSObject(inner ? inner : src, &iface)) { 999 if (iface && NS_SUCCEEDED(iface->QueryInterface(*iid, dest))) { 1000 return true; 1001 } 1002 1003 // If that failed, and iid is for mozIDOMWindowProxy, we actually 1004 // want the outer! 1005 if (iid->Equals(NS_GET_IID(mozIDOMWindowProxy))) { 1006 if (nsCOMPtr<mozIDOMWindow> inner = do_QueryInterface(iface)) { 1007 iface = nsPIDOMWindowInner::From(inner)->GetOuterWindow(); 1008 return NS_SUCCEEDED(iface->QueryInterface(*iid, dest)); 1009 } 1010 } 1011 1012 return false; 1013 } 1014 } 1015 1016 RefPtr<nsXPCWrappedJS> wrapper; 1017 nsresult rv = 1018 nsXPCWrappedJS::GetNewOrUsed(cx, src, *iid, getter_AddRefs(wrapper)); 1019 if (pErr) { 1020 *pErr = rv; 1021 } 1022 1023 if (NS_FAILED(rv) || !wrapper) { 1024 return false; 1025 } 1026 1027 // If the caller wanted to aggregate this JS object to a native, 1028 // attach it to the wrapper. Note that we allow a maximum of one 1029 // aggregated native for a given XPCWrappedJS. 1030 if (aOuter) { 1031 wrapper->SetAggregatedNativeObject(aOuter); 1032 } 1033 1034 // We need to go through the QueryInterface logic to make this return 1035 // the right thing for the various 'special' interfaces; e.g. 1036 // nsISimpleEnumerator. We must use AggregatedQueryInterface in cases where 1037 // there is an outer to avoid nasty recursion. 1038 rv = aOuter ? wrapper->AggregatedQueryInterface(*iid, dest) 1039 : wrapper->QueryInterface(*iid, dest); 1040 if (pErr) { 1041 *pErr = rv; 1042 } 1043 return NS_SUCCEEDED(rv); 1044 } 1045 1046 /***************************************************************************/ 1047 /***************************************************************************/ 1048 1049 // static 1050 nsresult XPCConvert::ConstructException(nsresult rv, const char* message, 1051 const char* ifaceName, 1052 const char* methodName, 1053 nsISupports* data, Exception** exceptn, 1054 JSContext* cx, Value* jsExceptionPtr) { 1055 MOZ_ASSERT(!cx == !jsExceptionPtr, 1056 "Expected cx and jsExceptionPtr to cooccur."); 1057 1058 static const char format[] = "\'%s\' when calling method: [%s::%s]"; 1059 const char* msg = message; 1060 nsAutoCString sxmsg; // must have the same lifetime as msg 1061 1062 nsCOMPtr<nsIScriptError> errorObject = do_QueryInterface(data); 1063 if (errorObject) { 1064 nsString xmsg; 1065 if (NS_SUCCEEDED(errorObject->GetMessageMoz(xmsg))) { 1066 CopyUTF16toUTF8(xmsg, sxmsg); 1067 msg = sxmsg.get(); 1068 } 1069 } 1070 if (!msg) { 1071 if (!nsXPCException::NameAndFormatForNSResult(rv, nullptr, &msg) || !msg) { 1072 msg = "<error>"; 1073 } 1074 } 1075 1076 nsCString msgStr(msg); 1077 if (ifaceName && methodName) { 1078 msgStr.AppendPrintf(format, msg, ifaceName, methodName); 1079 } 1080 1081 RefPtr<Exception> e = new Exception(msgStr, rv, ""_ns, nullptr, data); 1082 1083 if (cx && jsExceptionPtr) { 1084 e->StowJSVal(*jsExceptionPtr); 1085 } 1086 1087 e.forget(exceptn); 1088 return NS_OK; 1089 } 1090 1091 /********************************/ 1092 1093 class MOZ_STACK_CLASS AutoExceptionRestorer { 1094 public: 1095 AutoExceptionRestorer(JSContext* cx, const Value& v) 1096 : mContext(cx), tvr(cx, v) { 1097 JS_ClearPendingException(mContext); 1098 } 1099 1100 ~AutoExceptionRestorer() { JS_SetPendingException(mContext, tvr); } 1101 1102 private: 1103 JSContext* const mContext; 1104 RootedValue tvr; 1105 }; 1106 1107 static nsresult JSErrorToXPCException(JSContext* cx, const char* toStringResult, 1108 const char* ifaceName, 1109 const char* methodName, 1110 const JSErrorReport* report, 1111 Exception** exceptn) { 1112 nsresult rv = NS_ERROR_FAILURE; 1113 RefPtr<nsScriptError> data; 1114 if (report) { 1115 nsAutoString bestMessage; 1116 if (report->message()) { 1117 CopyUTF8toUTF16(mozilla::MakeStringSpan(report->message().c_str()), 1118 bestMessage); 1119 } else if (toStringResult) { 1120 CopyUTF8toUTF16(mozilla::MakeStringSpan(toStringResult), bestMessage); 1121 } else { 1122 bestMessage.AssignLiteral("JavaScript Error"); 1123 } 1124 1125 uint32_t flags = report->isWarning() ? nsIScriptError::warningFlag 1126 : nsIScriptError::errorFlag; 1127 1128 data = new nsScriptError(); 1129 data->nsIScriptError::InitWithWindowID( 1130 bestMessage, 1131 nsDependentCString(report->filename ? report->filename.c_str() : ""), 1132 report->lineno, report->column.oneOriginValue(), flags, 1133 "XPConnect JavaScript"_ns, 1134 nsJSUtils::GetCurrentlyRunningCodeInnerWindowID(cx)); 1135 } 1136 1137 if (data) { 1138 // Pass nullptr for the message: ConstructException will get a message 1139 // from the nsIScriptError. 1140 rv = XPCConvert::ConstructException( 1141 NS_ERROR_XPC_JAVASCRIPT_ERROR_WITH_DETAILS, nullptr, ifaceName, 1142 methodName, static_cast<nsIScriptError*>(data.get()), exceptn, nullptr, 1143 nullptr); 1144 } else { 1145 rv = XPCConvert::ConstructException(NS_ERROR_XPC_JAVASCRIPT_ERROR, nullptr, 1146 ifaceName, methodName, nullptr, exceptn, 1147 nullptr, nullptr); 1148 } 1149 return rv; 1150 } 1151 1152 // static 1153 nsresult XPCConvert::JSValToXPCException(JSContext* cx, MutableHandleValue s, 1154 const char* ifaceName, 1155 const char* methodName, 1156 Exception** exceptn) { 1157 AutoExceptionRestorer aer(cx, s); 1158 1159 if (!s.isPrimitive()) { 1160 // we have a JSObject 1161 RootedObject obj(cx, s.toObjectOrNull()); 1162 1163 if (!obj) { 1164 NS_ERROR("when is an object not an object?"); 1165 return NS_ERROR_FAILURE; 1166 } 1167 1168 // is this really a native xpcom object with a wrapper? 1169 JSObject* unwrapped = 1170 js::CheckedUnwrapDynamic(obj, cx, /* stopAtWindowProxy = */ false); 1171 if (!unwrapped) { 1172 return NS_ERROR_XPC_SECURITY_MANAGER_VETO; 1173 } 1174 // It's OK to use ReflectorToISupportsStatic, because we have already 1175 // stripped off wrappers. 1176 if (nsCOMPtr<nsISupports> supports = 1177 ReflectorToISupportsStatic(unwrapped)) { 1178 nsCOMPtr<Exception> iface = do_QueryInterface(supports); 1179 if (iface) { 1180 // just pass through the exception (with extra ref and all) 1181 iface.forget(exceptn); 1182 return NS_OK; 1183 } 1184 1185 // it is a wrapped native, but not an exception! 1186 return ConstructException(NS_ERROR_XPC_JS_THREW_NATIVE_OBJECT, nullptr, 1187 ifaceName, methodName, supports, exceptn, 1188 nullptr, nullptr); 1189 } else { 1190 // It is a JSObject, but not a wrapped native... 1191 1192 // If it is an engine Error with an error report then let's 1193 // extract the report and build an xpcexception from that 1194 const JSErrorReport* report; 1195 if (nullptr != (report = JS_ErrorFromException(cx, obj))) { 1196 JS::UniqueChars toStringResult; 1197 RootedString str(cx, ToString(cx, s)); 1198 if (str) { 1199 toStringResult = JS_EncodeStringToUTF8(cx, str); 1200 } 1201 return JSErrorToXPCException(cx, toStringResult.get(), ifaceName, 1202 methodName, report, exceptn); 1203 } 1204 1205 // XXX we should do a check against 'js_ErrorClass' here and 1206 // do the right thing - even though it has no JSErrorReport, 1207 // The fact that it is a JSError exceptions means we can extract 1208 // particular info and our 'result' should reflect that. 1209 1210 // otherwise we'll just try to convert it to a string 1211 1212 JSString* str = ToString(cx, s); 1213 if (!str) { 1214 return NS_ERROR_FAILURE; 1215 } 1216 1217 JS::UniqueChars strBytes = JS_EncodeStringToLatin1(cx, str); 1218 if (!strBytes) { 1219 return NS_ERROR_FAILURE; 1220 } 1221 1222 return ConstructException(NS_ERROR_XPC_JS_THREW_JS_OBJECT, strBytes.get(), 1223 ifaceName, methodName, nullptr, exceptn, cx, 1224 s.address()); 1225 } 1226 } 1227 1228 if (s.isUndefined() || s.isNull()) { 1229 return ConstructException(NS_ERROR_XPC_JS_THREW_NULL, nullptr, ifaceName, 1230 methodName, nullptr, exceptn, cx, s.address()); 1231 } 1232 1233 if (s.isNumber()) { 1234 // lets see if it looks like an nsresult 1235 nsresult rv; 1236 double number; 1237 bool isResult = false; 1238 1239 if (s.isInt32()) { 1240 rv = (nsresult)s.toInt32(); 1241 if (NS_FAILED(rv)) { 1242 isResult = true; 1243 } else { 1244 number = (double)s.toInt32(); 1245 } 1246 } else { 1247 number = s.toDouble(); 1248 if (number > 0.0 && number < (double)0xffffffff && 1249 0.0 == fmod(number, 1)) { 1250 // Visual Studio 9 doesn't allow casting directly from a 1251 // double to an enumeration type, contrary to 5.2.9(10) of 1252 // C++11, so add an intermediate cast. 1253 rv = (nsresult)(uint32_t)number; 1254 if (NS_FAILED(rv)) { 1255 isResult = true; 1256 } 1257 } 1258 } 1259 1260 if (isResult) { 1261 return ConstructException(rv, nullptr, ifaceName, methodName, nullptr, 1262 exceptn, cx, s.address()); 1263 } else { 1264 // XXX all this nsISupportsDouble code seems a little redundant 1265 // now that we're storing the Value in the exception... 1266 nsCOMPtr<nsISupportsDouble> data; 1267 nsCOMPtr<nsIComponentManager> cm; 1268 if (NS_FAILED(NS_GetComponentManager(getter_AddRefs(cm))) || !cm || 1269 NS_FAILED(cm->CreateInstanceByContractID( 1270 NS_SUPPORTS_DOUBLE_CONTRACTID, NS_GET_IID(nsISupportsDouble), 1271 getter_AddRefs(data)))) { 1272 return NS_ERROR_FAILURE; 1273 } 1274 data->SetData(number); 1275 rv = ConstructException(NS_ERROR_XPC_JS_THREW_NUMBER, nullptr, ifaceName, 1276 methodName, data, exceptn, cx, s.address()); 1277 return rv; 1278 } 1279 } 1280 1281 // otherwise we'll just try to convert it to a string 1282 // Note: e.g., bools get converted to JSStrings by this code. 1283 1284 JSString* str = ToString(cx, s); 1285 if (str) { 1286 if (JS::UniqueChars strBytes = JS_EncodeStringToLatin1(cx, str)) { 1287 return ConstructException(NS_ERROR_XPC_JS_THREW_STRING, strBytes.get(), 1288 ifaceName, methodName, nullptr, exceptn, cx, 1289 s.address()); 1290 } 1291 } 1292 return NS_ERROR_FAILURE; 1293 } 1294 1295 /***************************************************************************/ 1296 1297 // array fun... 1298 1299 // static 1300 bool XPCConvert::NativeArray2JS(JSContext* cx, MutableHandleValue d, 1301 const void* buf, const nsXPTType& type, 1302 const nsID* iid, uint32_t count, 1303 nsresult* pErr) { 1304 MOZ_ASSERT(buf || count == 0, "Must have buf or 0 elements"); 1305 1306 RootedObject array(cx, JS::NewArrayObject(cx, count)); 1307 if (!array) { 1308 return false; 1309 } 1310 1311 if (pErr) { 1312 *pErr = NS_ERROR_XPC_BAD_CONVERT_NATIVE; 1313 } 1314 1315 RootedValue current(cx, JS::NullValue()); 1316 for (uint32_t i = 0; i < count; ++i) { 1317 if (!NativeData2JS(cx, ¤t, type.ElementPtr(buf, i), type, iid, 0, 1318 pErr) || 1319 !JS_DefineElement(cx, array, i, current, JSPROP_ENUMERATE)) 1320 return false; 1321 } 1322 1323 if (pErr) { 1324 *pErr = NS_OK; 1325 } 1326 d.setObject(*array); 1327 return true; 1328 } 1329 1330 // static 1331 bool XPCConvert::JSArray2Native(JSContext* cx, JS::HandleValue aJSVal, 1332 const nsXPTType& aEltType, const nsIID* aIID, 1333 nsresult* pErr, 1334 const ArrayAllocFixupLen& aAllocFixupLen) { 1335 // Wrap aAllocFixupLen to check length is within bounds & initialize the 1336 // allocated memory if needed. 1337 auto allocFixupLen = [&](uint32_t* aLength) -> void* { 1338 if (*aLength > (UINT32_MAX / aEltType.Stride())) { 1339 return nullptr; // Byte length doesn't fit in uint32_t 1340 } 1341 1342 void* buf = aAllocFixupLen(aLength); 1343 1344 // Ensure the buffer has valid values for each element. We can skip this 1345 // for arithmetic types, as they do not require initialization. 1346 if (buf && !aEltType.IsArithmetic()) { 1347 for (uint32_t i = 0; i < *aLength; ++i) { 1348 InitializeValue(aEltType, aEltType.ElementPtr(buf, i)); 1349 } 1350 } 1351 return buf; 1352 }; 1353 1354 // JSArray2Native only accepts objects (Array and TypedArray). 1355 if (!aJSVal.isObject()) { 1356 if (pErr) { 1357 *pErr = NS_ERROR_XPC_CANT_CONVERT_PRIMITIVE_TO_ARRAY; 1358 } 1359 return false; 1360 } 1361 RootedObject jsarray(cx, &aJSVal.toObject()); 1362 1363 if (pErr) { 1364 *pErr = NS_ERROR_XPC_BAD_CONVERT_JS; 1365 } 1366 1367 if (JS_IsTypedArrayObject(jsarray)) { 1368 // Fast conversion of typed arrays to native using memcpy. No float or 1369 // double canonicalization is done. ArrayBuffers are not accepted; 1370 // create a properly typed array view on them first. The element type of 1371 // array must match the XPCOM type in size, type and signedness exactly. 1372 // As an exception, Uint8ClampedArray is allowed for arrays of uint8_t. 1373 // DataViews are not supported. 1374 1375 nsXPTTypeTag tag; 1376 switch (JS_GetArrayBufferViewType(jsarray)) { 1377 case js::Scalar::Int8: 1378 tag = TD_INT8; 1379 break; 1380 case js::Scalar::Uint8: 1381 tag = TD_UINT8; 1382 break; 1383 case js::Scalar::Uint8Clamped: 1384 tag = TD_UINT8; 1385 break; 1386 case js::Scalar::Int16: 1387 tag = TD_INT16; 1388 break; 1389 case js::Scalar::Uint16: 1390 tag = TD_UINT16; 1391 break; 1392 case js::Scalar::Int32: 1393 tag = TD_INT32; 1394 break; 1395 case js::Scalar::Uint32: 1396 tag = TD_UINT32; 1397 break; 1398 case js::Scalar::Float32: 1399 tag = TD_FLOAT; 1400 break; 1401 case js::Scalar::Float64: 1402 tag = TD_DOUBLE; 1403 break; 1404 default: 1405 return false; 1406 } 1407 if (aEltType.Tag() != tag) { 1408 return false; 1409 } 1410 1411 // Allocate the backing buffer before getting the view data in case 1412 // allocFixupLen can cause GCs. 1413 uint32_t length; 1414 { 1415 // nsTArray and code below uses uint32_t lengths, so reject large typed 1416 // arrays. 1417 size_t fullLength = JS_GetTypedArrayLength(jsarray); 1418 if (fullLength > UINT32_MAX) { 1419 return false; 1420 } 1421 length = uint32_t(fullLength); 1422 } 1423 void* buf = allocFixupLen(&length); 1424 if (!buf) { 1425 return false; 1426 } 1427 1428 // Get the backing memory buffer to copy out of. 1429 JS::AutoCheckCannotGC nogc; 1430 bool isShared = false; 1431 const void* data = JS_GetArrayBufferViewData(jsarray, &isShared, nogc); 1432 1433 // Require opting in to shared memory - a future project. 1434 if (isShared) { 1435 return false; 1436 } 1437 1438 // Directly copy data into the allocated target buffer. 1439 memcpy(buf, data, length * aEltType.Stride()); 1440 return true; 1441 } 1442 1443 // If jsarray is not a TypedArrayObject, check for an Array object. 1444 uint32_t length = 0; 1445 bool isArray = false; 1446 if (!JS::IsArrayObject(cx, jsarray, &isArray) || !isArray || 1447 !JS::GetArrayLength(cx, jsarray, &length)) { 1448 if (pErr) { 1449 *pErr = NS_ERROR_XPC_CANT_CONVERT_OBJECT_TO_ARRAY; 1450 } 1451 return false; 1452 } 1453 1454 void* buf = allocFixupLen(&length); 1455 if (!buf) { 1456 return false; 1457 } 1458 1459 // Translate each array element separately. 1460 RootedValue current(cx); 1461 for (uint32_t i = 0; i < length; ++i) { 1462 if (!JS_GetElement(cx, jsarray, i, ¤t) || 1463 !JSData2Native(cx, aEltType.ElementPtr(buf, i), current, aEltType, aIID, 1464 0, pErr)) { 1465 // Array element conversion failed. Clean up all elements converted 1466 // before the error. Caller handles freeing 'buf'. 1467 for (uint32_t j = 0; j < i; ++j) { 1468 DestructValue(aEltType, aEltType.ElementPtr(buf, j)); 1469 } 1470 return false; 1471 } 1472 } 1473 1474 return true; 1475 } 1476 1477 /***************************************************************************/ 1478 1479 // Internal implementation details for xpc::CleanupValue. 1480 1481 void xpc::InnerCleanupValue(const nsXPTType& aType, void* aValue, 1482 uint32_t aArrayLen) { 1483 MOZ_ASSERT(!aType.IsArithmetic(), 1484 "Arithmetic types should not get to InnerCleanupValue!"); 1485 MOZ_ASSERT(aArrayLen == 0 || aType.Tag() == nsXPTType::T_PSTRING_SIZE_IS || 1486 aType.Tag() == nsXPTType::T_PWSTRING_SIZE_IS || 1487 aType.Tag() == nsXPTType::T_LEGACY_ARRAY, 1488 "Array lengths may only appear for certain types!"); 1489 1490 switch (aType.Tag()) { 1491 // Pointer types 1492 case nsXPTType::T_DOMOBJECT: 1493 aType.GetDOMObjectInfo().Cleanup(*(void**)aValue); 1494 break; 1495 1496 case nsXPTType::T_PROMISE: 1497 (*(mozilla::dom::Promise**)aValue)->Release(); 1498 break; 1499 1500 case nsXPTType::T_INTERFACE: 1501 case nsXPTType::T_INTERFACE_IS: 1502 (*(nsISupports**)aValue)->Release(); 1503 break; 1504 1505 // String types 1506 case nsXPTType::T_ASTRING: 1507 ((nsAString*)aValue)->Truncate(); 1508 break; 1509 case nsXPTType::T_UTF8STRING: 1510 case nsXPTType::T_CSTRING: 1511 ((nsACString*)aValue)->Truncate(); 1512 break; 1513 1514 // Pointer Types 1515 case nsXPTType::T_NSIDPTR: 1516 case nsXPTType::T_CHAR_STR: 1517 case nsXPTType::T_WCHAR_STR: 1518 case nsXPTType::T_PSTRING_SIZE_IS: 1519 case nsXPTType::T_PWSTRING_SIZE_IS: 1520 free(*(void**)aValue); 1521 break; 1522 1523 // Legacy Array Type 1524 case nsXPTType::T_LEGACY_ARRAY: { 1525 const nsXPTType& elty = aType.ArrayElementType(); 1526 void* elements = *(void**)aValue; 1527 1528 for (uint32_t i = 0; i < aArrayLen; ++i) { 1529 DestructValue(elty, elty.ElementPtr(elements, i)); 1530 } 1531 free(elements); 1532 break; 1533 } 1534 1535 // Array Type 1536 case nsXPTType::T_ARRAY: { 1537 const nsXPTType& elty = aType.ArrayElementType(); 1538 auto* array = (xpt::detail::UntypedTArray*)aValue; 1539 1540 for (uint32_t i = 0; i < array->Length(); ++i) { 1541 DestructValue(elty, elty.ElementPtr(array->Elements(), i)); 1542 } 1543 array->Clear(); 1544 break; 1545 } 1546 1547 // Clear nsID& parameters to `0` 1548 case nsXPTType::T_NSID: 1549 ((nsID*)aValue)->Clear(); 1550 break; 1551 1552 // Clear the JS::Value to `undefined` 1553 case nsXPTType::T_JSVAL: 1554 ((JS::Value*)aValue)->setUndefined(); 1555 break; 1556 1557 // Non-arithmetic types requiring no cleanup 1558 case nsXPTType::T_VOID: 1559 break; 1560 1561 default: 1562 MOZ_CRASH("Unknown Type!"); 1563 } 1564 1565 // Clear any non-complex values to the valid '0' state. 1566 if (!aType.IsComplex()) { 1567 aType.ZeroValue(aValue); 1568 } 1569 } 1570 1571 /***************************************************************************/ 1572 1573 // Implementation of xpc::InitializeValue. 1574 1575 void xpc::InitializeValue(const nsXPTType& aType, void* aValue) { 1576 switch (aType.Tag()) { 1577 // Use placement-new to initialize complex values 1578 #define XPT_INIT_TYPE(tag, type) \ 1579 case tag: \ 1580 new (aValue) type(); \ 1581 break; 1582 XPT_FOR_EACH_COMPLEX_TYPE(XPT_INIT_TYPE) 1583 #undef XPT_INIT_TYPE 1584 1585 // The remaining types have valid states where all bytes are '0'. 1586 default: 1587 aType.ZeroValue(aValue); 1588 break; 1589 } 1590 } 1591 1592 // In XPT_FOR_EACH_COMPLEX_TYPE, typenames may be namespaced (such as 1593 // xpt::UntypedTArray). Namespaced typenames cannot be used to explicitly invoke 1594 // destructors, so this method acts as a helper to let us call the destructor of 1595 // these objects. 1596 template <typename T> 1597 static void _DestructValueHelper(void* aValue) { 1598 static_cast<T*>(aValue)->~T(); 1599 } 1600 1601 void xpc::DestructValue(const nsXPTType& aType, void* aValue, 1602 uint32_t aArrayLen) { 1603 // Get aValue into an clean, empty state. 1604 xpc::CleanupValue(aType, aValue, aArrayLen); 1605 1606 // Run destructors on complex types. 1607 switch (aType.Tag()) { 1608 #define XPT_RUN_DESTRUCTOR(tag, type) \ 1609 case tag: \ 1610 _DestructValueHelper<type>(aValue); \ 1611 break; 1612 XPT_FOR_EACH_COMPLEX_TYPE(XPT_RUN_DESTRUCTOR) 1613 #undef XPT_RUN_DESTRUCTOR 1614 default: 1615 break; // dtor is a no-op on other types. 1616 } 1617 }