js/xpconnect/src/XPCConvert.cpp

Thu, 15 Jan 2015 15:55:04 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 15 Jan 2015 15:55:04 +0100
branch
TOR_BUG_9701
changeset 9
a63d609f5ebe
permissions
-rw-r--r--

Back out 97036ab72558 which inappropriately compared turds to third parties.

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

mercurial