js/xpconnect/src/XPCConvert.cpp

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/js/xpconnect/src/XPCConvert.cpp	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,1878 @@
     1.4 +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
     1.5 +/* vim: set ts=8 sts=4 et sw=4 tw=99: */
     1.6 +/* This Source Code Form is subject to the terms of the Mozilla Public
     1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this
     1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     1.9 +
    1.10 +/* Data conversion between native and JavaScript types. */
    1.11 +
    1.12 +#include "mozilla/ArrayUtils.h"
    1.13 +
    1.14 +#include "xpcprivate.h"
    1.15 +#include "nsIAtom.h"
    1.16 +#include "nsWrapperCache.h"
    1.17 +#include "nsJSUtils.h"
    1.18 +#include "WrapperFactory.h"
    1.19 +
    1.20 +#include "nsWrapperCacheInlines.h"
    1.21 +
    1.22 +#include "jsapi.h"
    1.23 +#include "jsfriendapi.h"
    1.24 +#include "jsprf.h"
    1.25 +#include "JavaScriptParent.h"
    1.26 +
    1.27 +#include "mozilla/dom/BindingUtils.h"
    1.28 +#include "mozilla/dom/DOMException.h"
    1.29 +#include "mozilla/dom/PrimitiveConversions.h"
    1.30 +
    1.31 +using namespace xpc;
    1.32 +using namespace mozilla;
    1.33 +using namespace mozilla::dom;
    1.34 +using namespace JS;
    1.35 +
    1.36 +//#define STRICT_CHECK_OF_UNICODE
    1.37 +#ifdef STRICT_CHECK_OF_UNICODE
    1.38 +#define ILLEGAL_RANGE(c) (0!=((c) & 0xFF80))
    1.39 +#else // STRICT_CHECK_OF_UNICODE
    1.40 +#define ILLEGAL_RANGE(c) (0!=((c) & 0xFF00))
    1.41 +#endif // STRICT_CHECK_OF_UNICODE
    1.42 +
    1.43 +#define ILLEGAL_CHAR_RANGE(c) (0!=((c) & 0x80))
    1.44 +
    1.45 +/***********************************************************/
    1.46 +
    1.47 +// static
    1.48 +bool
    1.49 +XPCConvert::IsMethodReflectable(const XPTMethodDescriptor& info)
    1.50 +{
    1.51 +    if (XPT_MD_IS_NOTXPCOM(info.flags) || XPT_MD_IS_HIDDEN(info.flags))
    1.52 +        return false;
    1.53 +
    1.54 +    for (int i = info.num_args-1; i >= 0; i--) {
    1.55 +        const nsXPTParamInfo& param = info.params[i];
    1.56 +        const nsXPTType& type = param.GetType();
    1.57 +
    1.58 +        // Reflected methods can't use native types. All native types end up
    1.59 +        // getting tagged as void*, so this check is easy.
    1.60 +        if (type.TagPart() == nsXPTType::T_VOID)
    1.61 +            return false;
    1.62 +    }
    1.63 +    return true;
    1.64 +}
    1.65 +
    1.66 +static JSObject*
    1.67 +UnwrapNativeCPOW(nsISupports* wrapper)
    1.68 +{
    1.69 +    nsCOMPtr<nsIXPConnectWrappedJS> underware = do_QueryInterface(wrapper);
    1.70 +    if (underware) {
    1.71 +        JSObject* mainObj = underware->GetJSObject();
    1.72 +        if (mainObj && mozilla::jsipc::JavaScriptParent::IsCPOW(mainObj))
    1.73 +            return mainObj;
    1.74 +    }
    1.75 +    return nullptr;
    1.76 +}
    1.77 +
    1.78 +/***************************************************************************/
    1.79 +
    1.80 +// static
    1.81 +bool
    1.82 +XPCConvert::GetISupportsFromJSObject(JSObject* obj, nsISupports** iface)
    1.83 +{
    1.84 +    const JSClass* jsclass = js::GetObjectJSClass(obj);
    1.85 +    MOZ_ASSERT(jsclass, "obj has no class");
    1.86 +    if (jsclass &&
    1.87 +        (jsclass->flags & JSCLASS_HAS_PRIVATE) &&
    1.88 +        (jsclass->flags & JSCLASS_PRIVATE_IS_NSISUPPORTS)) {
    1.89 +        *iface = (nsISupports*) xpc_GetJSPrivate(obj);
    1.90 +        return true;
    1.91 +    }
    1.92 +    *iface = UnwrapDOMObjectToISupports(obj);
    1.93 +    return !!*iface;
    1.94 +}
    1.95 +
    1.96 +/***************************************************************************/
    1.97 +
    1.98 +// static
    1.99 +bool
   1.100 +XPCConvert::NativeData2JS(MutableHandleValue d, const void* s,
   1.101 +                          const nsXPTType& type, const nsID* iid, nsresult* pErr)
   1.102 +{
   1.103 +    NS_PRECONDITION(s, "bad param");
   1.104 +
   1.105 +    AutoJSContext cx;
   1.106 +    if (pErr)
   1.107 +        *pErr = NS_ERROR_XPC_BAD_CONVERT_NATIVE;
   1.108 +
   1.109 +    switch (type.TagPart()) {
   1.110 +    case nsXPTType::T_I8    :
   1.111 +        d.setInt32(*static_cast<const int8_t*>(s));
   1.112 +        return true;
   1.113 +    case nsXPTType::T_I16   :
   1.114 +        d.setInt32(*static_cast<const int16_t*>(s));
   1.115 +        return true;
   1.116 +    case nsXPTType::T_I32   :
   1.117 +        d.setInt32(*static_cast<const int32_t*>(s));
   1.118 +        return true;
   1.119 +    case nsXPTType::T_I64   :
   1.120 +        d.setNumber(static_cast<double>(*static_cast<const int64_t*>(s)));
   1.121 +        return true;
   1.122 +    case nsXPTType::T_U8    :
   1.123 +        d.setInt32(*static_cast<const uint8_t*>(s));
   1.124 +        return true;
   1.125 +    case nsXPTType::T_U16   :
   1.126 +        d.setInt32(*static_cast<const uint16_t*>(s));
   1.127 +        return true;
   1.128 +    case nsXPTType::T_U32   :
   1.129 +        d.setNumber(*static_cast<const uint32_t*>(s));
   1.130 +        return true;
   1.131 +    case nsXPTType::T_U64   :
   1.132 +        d.setNumber(static_cast<double>(*static_cast<const uint64_t*>(s)));
   1.133 +        return true;
   1.134 +    case nsXPTType::T_FLOAT :
   1.135 +        d.setNumber(*static_cast<const float*>(s));
   1.136 +        return true;
   1.137 +    case nsXPTType::T_DOUBLE:
   1.138 +        d.setNumber(*static_cast<const double*>(s));
   1.139 +        return true;
   1.140 +    case nsXPTType::T_BOOL  :
   1.141 +        d.setBoolean(*static_cast<const bool*>(s));
   1.142 +        return true;
   1.143 +    case nsXPTType::T_CHAR  :
   1.144 +    {
   1.145 +        char p = *static_cast<const char*>(s);
   1.146 +
   1.147 +#ifdef STRICT_CHECK_OF_UNICODE
   1.148 +        MOZ_ASSERT(! ILLEGAL_CHAR_RANGE(p) , "passing non ASCII data");
   1.149 +#endif // STRICT_CHECK_OF_UNICODE
   1.150 +
   1.151 +        JSString* str = JS_NewStringCopyN(cx, &p, 1);
   1.152 +        if (!str)
   1.153 +            return false;
   1.154 +
   1.155 +        d.setString(str);
   1.156 +        return true;
   1.157 +    }
   1.158 +    case nsXPTType::T_WCHAR :
   1.159 +    {
   1.160 +        jschar p = *static_cast<const jschar*>(s);
   1.161 +
   1.162 +        JSString* str = JS_NewUCStringCopyN(cx, &p, 1);
   1.163 +        if (!str)
   1.164 +            return false;
   1.165 +
   1.166 +        d.setString(str);
   1.167 +        return true;
   1.168 +    }
   1.169 +
   1.170 +    case nsXPTType::T_JSVAL :
   1.171 +    {
   1.172 +        d.set(*static_cast<const Value*>(s));
   1.173 +        return JS_WrapValue(cx, d);
   1.174 +    }
   1.175 +
   1.176 +    case nsXPTType::T_VOID:
   1.177 +        XPC_LOG_ERROR(("XPCConvert::NativeData2JS : void* params not supported"));
   1.178 +        return false;
   1.179 +
   1.180 +    case nsXPTType::T_IID:
   1.181 +    {
   1.182 +        nsID* iid2 = *static_cast<nsID* const *>(s);
   1.183 +        if (!iid2) {
   1.184 +            d.setNull();
   1.185 +            return true;
   1.186 +        }
   1.187 +
   1.188 +        RootedObject scope(cx, JS::CurrentGlobalOrNull(cx));
   1.189 +        JSObject* obj = xpc_NewIDObject(cx, scope, *iid2);
   1.190 +        if (!obj)
   1.191 +            return false;
   1.192 +
   1.193 +        d.setObject(*obj);
   1.194 +        return true;
   1.195 +    }
   1.196 +
   1.197 +    case nsXPTType::T_ASTRING:
   1.198 +        // Fall through to T_DOMSTRING case
   1.199 +
   1.200 +    case nsXPTType::T_DOMSTRING:
   1.201 +    {
   1.202 +        const nsAString* p = *static_cast<const nsAString* const *>(s);
   1.203 +        if (!p || p->IsVoid()) {
   1.204 +            d.setNull();
   1.205 +            return true;
   1.206 +        }
   1.207 +
   1.208 +        nsStringBuffer* buf;
   1.209 +        if (!XPCStringConvert::ReadableToJSVal(cx, *p, &buf, d))
   1.210 +            return false;
   1.211 +        if (buf)
   1.212 +            buf->AddRef();
   1.213 +        return true;
   1.214 +    }
   1.215 +
   1.216 +    case nsXPTType::T_CHAR_STR:
   1.217 +    {
   1.218 +        const char* p = *static_cast<const char* const *>(s);
   1.219 +        if (!p) {
   1.220 +            d.setNull();
   1.221 +            return true;
   1.222 +        }
   1.223 +
   1.224 +#ifdef STRICT_CHECK_OF_UNICODE
   1.225 +        bool isAscii = true;
   1.226 +        for (char* t = p; *t && isAscii; t++) {
   1.227 +          if (ILLEGAL_CHAR_RANGE(*t))
   1.228 +              isAscii = false;
   1.229 +        }
   1.230 +        MOZ_ASSERT(isAscii, "passing non ASCII data");
   1.231 +#endif // STRICT_CHECK_OF_UNICODE
   1.232 +
   1.233 +        JSString* str = JS_NewStringCopyZ(cx, p);
   1.234 +        if (!str)
   1.235 +            return false;
   1.236 +
   1.237 +        d.setString(str);
   1.238 +        return true;
   1.239 +    }
   1.240 +
   1.241 +    case nsXPTType::T_WCHAR_STR:
   1.242 +    {
   1.243 +        const jschar* p = *static_cast<const jschar* const *>(s);
   1.244 +        if (!p) {
   1.245 +            d.setNull();
   1.246 +            return true;
   1.247 +        }
   1.248 +
   1.249 +        JSString* str = JS_NewUCStringCopyZ(cx, p);
   1.250 +        if (!str)
   1.251 +            return false;
   1.252 +
   1.253 +        d.setString(str);
   1.254 +        return true;
   1.255 +    }
   1.256 +    case nsXPTType::T_UTF8STRING:
   1.257 +    {
   1.258 +        const nsACString* utf8String = *static_cast<const nsACString* const *>(s);
   1.259 +
   1.260 +        if (!utf8String || utf8String->IsVoid()) {
   1.261 +            d.setNull();
   1.262 +            return true;
   1.263 +        }
   1.264 +
   1.265 +        if (utf8String->IsEmpty()) {
   1.266 +            d.set(JS_GetEmptyStringValue(cx));
   1.267 +            return true;
   1.268 +        }
   1.269 +
   1.270 +        const uint32_t len = CalcUTF8ToUnicodeLength(*utf8String);
   1.271 +        // The cString is not empty at this point, but the calculated
   1.272 +        // UTF-16 length is zero, meaning no valid conversion exists.
   1.273 +        if (!len)
   1.274 +            return false;
   1.275 +
   1.276 +        const size_t buffer_size = (len + 1) * sizeof(char16_t);
   1.277 +        char16_t* buffer =
   1.278 +            static_cast<char16_t*>(JS_malloc(cx, buffer_size));
   1.279 +        if (!buffer)
   1.280 +            return false;
   1.281 +
   1.282 +        uint32_t copied;
   1.283 +        if (!UTF8ToUnicodeBuffer(*utf8String, buffer, &copied) ||
   1.284 +            len != copied) {
   1.285 +            // Copy or conversion during copy failed. Did not copy the
   1.286 +            // whole string.
   1.287 +            JS_free(cx, buffer);
   1.288 +            return false;
   1.289 +        }
   1.290 +
   1.291 +        // JS_NewUCString takes ownership on success, i.e. a
   1.292 +        // successful call will make it the responsiblity of the JS VM
   1.293 +        // to free the buffer.
   1.294 +        JSString* str = JS_NewUCString(cx, buffer, len);
   1.295 +        if (!str) {
   1.296 +            JS_free(cx, buffer);
   1.297 +            return false;
   1.298 +        }
   1.299 +
   1.300 +        d.setString(str);
   1.301 +        return true;
   1.302 +    }
   1.303 +    case nsXPTType::T_CSTRING:
   1.304 +    {
   1.305 +        const nsACString* cString = *static_cast<const nsACString* const *>(s);
   1.306 +
   1.307 +        if (!cString || cString->IsVoid()) {
   1.308 +            d.setNull();
   1.309 +            return true;
   1.310 +        }
   1.311 +
   1.312 +        // c-strings (binary blobs) are deliberately not converted from
   1.313 +        // UTF-8 to UTF-16. T_UTF8Sting is for UTF-8 encoded strings
   1.314 +        // with automatic conversion.
   1.315 +        JSString* str = JS_NewStringCopyN(cx, cString->Data(),
   1.316 +                                          cString->Length());
   1.317 +        if (!str)
   1.318 +            return false;
   1.319 +
   1.320 +        d.setString(str);
   1.321 +        return true;
   1.322 +    }
   1.323 +
   1.324 +    case nsXPTType::T_INTERFACE:
   1.325 +    case nsXPTType::T_INTERFACE_IS:
   1.326 +    {
   1.327 +        nsISupports* iface = *static_cast<nsISupports* const *>(s);
   1.328 +        if (!iface) {
   1.329 +            d.setNull();
   1.330 +            return true;
   1.331 +        }
   1.332 +
   1.333 +        if (iid->Equals(NS_GET_IID(nsIVariant))) {
   1.334 +            nsCOMPtr<nsIVariant> variant = do_QueryInterface(iface);
   1.335 +            if (!variant)
   1.336 +                return false;
   1.337 +
   1.338 +            return XPCVariant::VariantDataToJS(variant,
   1.339 +                                               pErr, d);
   1.340 +        }
   1.341 +
   1.342 +        xpcObjectHelper helper(iface);
   1.343 +        return NativeInterface2JSObject(d, nullptr, helper, iid, nullptr, true, pErr);
   1.344 +    }
   1.345 +
   1.346 +    default:
   1.347 +        NS_ERROR("bad type");
   1.348 +        return false;
   1.349 +    }
   1.350 +    return true;
   1.351 +}
   1.352 +
   1.353 +/***************************************************************************/
   1.354 +
   1.355 +#ifdef DEBUG
   1.356 +static bool
   1.357 +CheckJSCharInCharRange(jschar c)
   1.358 +{
   1.359 +    if (ILLEGAL_RANGE(c)) {
   1.360 +        /* U+0080/U+0100 - U+FFFF data lost. */
   1.361 +        static const size_t MSG_BUF_SIZE = 64;
   1.362 +        char msg[MSG_BUF_SIZE];
   1.363 +        JS_snprintf(msg, MSG_BUF_SIZE, "jschar out of char range; high bits of data lost: 0x%x", c);
   1.364 +        NS_WARNING(msg);
   1.365 +        return false;
   1.366 +    }
   1.367 +
   1.368 +    return true;
   1.369 +}
   1.370 +#endif
   1.371 +
   1.372 +template<typename T>
   1.373 +bool ConvertToPrimitive(JSContext *cx, HandleValue v, T *retval)
   1.374 +{
   1.375 +    return ValueToPrimitive<T, eDefault>(cx, v, retval);
   1.376 +}
   1.377 +
   1.378 +// static
   1.379 +bool
   1.380 +XPCConvert::JSData2Native(void* d, HandleValue s,
   1.381 +                          const nsXPTType& type,
   1.382 +                          bool useAllocator, const nsID* iid,
   1.383 +                          nsresult* pErr)
   1.384 +{
   1.385 +    NS_PRECONDITION(d, "bad param");
   1.386 +
   1.387 +    AutoJSContext cx;
   1.388 +    if (pErr)
   1.389 +        *pErr = NS_ERROR_XPC_BAD_CONVERT_JS;
   1.390 +
   1.391 +    switch (type.TagPart()) {
   1.392 +    case nsXPTType::T_I8     :
   1.393 +        return ConvertToPrimitive(cx, s, static_cast<int8_t*>(d));
   1.394 +    case nsXPTType::T_I16    :
   1.395 +        return ConvertToPrimitive(cx, s, static_cast<int16_t*>(d));
   1.396 +    case nsXPTType::T_I32    :
   1.397 +        return ConvertToPrimitive(cx, s, static_cast<int32_t*>(d));
   1.398 +    case nsXPTType::T_I64    :
   1.399 +        return ConvertToPrimitive(cx, s, static_cast<int64_t*>(d));
   1.400 +    case nsXPTType::T_U8     :
   1.401 +        return ConvertToPrimitive(cx, s, static_cast<uint8_t*>(d));
   1.402 +    case nsXPTType::T_U16    :
   1.403 +        return ConvertToPrimitive(cx, s, static_cast<uint16_t*>(d));
   1.404 +    case nsXPTType::T_U32    :
   1.405 +        return ConvertToPrimitive(cx, s, static_cast<uint32_t*>(d));
   1.406 +    case nsXPTType::T_U64    :
   1.407 +        return ConvertToPrimitive(cx, s, static_cast<uint64_t*>(d));
   1.408 +    case nsXPTType::T_FLOAT  :
   1.409 +        return ConvertToPrimitive(cx, s, static_cast<float*>(d));
   1.410 +    case nsXPTType::T_DOUBLE :
   1.411 +        return ConvertToPrimitive(cx, s, static_cast<double*>(d));
   1.412 +    case nsXPTType::T_BOOL   :
   1.413 +        return ConvertToPrimitive(cx, s, static_cast<bool*>(d));
   1.414 +    case nsXPTType::T_CHAR   :
   1.415 +    {
   1.416 +        JSString* str = ToString(cx, s);
   1.417 +        if (!str) {
   1.418 +            return false;
   1.419 +        }
   1.420 +        size_t length;
   1.421 +        const jschar* chars = JS_GetStringCharsAndLength(cx, str, &length);
   1.422 +        if (!chars) {
   1.423 +            return false;
   1.424 +        }
   1.425 +        jschar ch = length ? chars[0] : 0;
   1.426 +#ifdef DEBUG
   1.427 +        CheckJSCharInCharRange(ch);
   1.428 +#endif
   1.429 +        *((char*)d) = char(ch);
   1.430 +        break;
   1.431 +    }
   1.432 +    case nsXPTType::T_WCHAR  :
   1.433 +    {
   1.434 +        JSString* str;
   1.435 +        if (!(str = ToString(cx, s))) {
   1.436 +            return false;
   1.437 +        }
   1.438 +        size_t length;
   1.439 +        const jschar* chars = JS_GetStringCharsAndLength(cx, str, &length);
   1.440 +        if (!chars) {
   1.441 +            return false;
   1.442 +        }
   1.443 +        if (length == 0) {
   1.444 +            *((uint16_t*)d) = 0;
   1.445 +            break;
   1.446 +        }
   1.447 +        *((uint16_t*)d) = uint16_t(chars[0]);
   1.448 +        break;
   1.449 +    }
   1.450 +    case nsXPTType::T_JSVAL :
   1.451 +        *((jsval*)d) = s;
   1.452 +        break;
   1.453 +    case nsXPTType::T_VOID:
   1.454 +        XPC_LOG_ERROR(("XPCConvert::JSData2Native : void* params not supported"));
   1.455 +        NS_ERROR("void* params not supported");
   1.456 +        return false;
   1.457 +    case nsXPTType::T_IID:
   1.458 +    {
   1.459 +        const nsID* pid = nullptr;
   1.460 +
   1.461 +        // There's no good reason to pass a null IID.
   1.462 +        if (s.isNullOrUndefined()) {
   1.463 +            if (pErr)
   1.464 +                *pErr = NS_ERROR_XPC_BAD_CONVERT_JS;
   1.465 +            return false;
   1.466 +        }
   1.467 +
   1.468 +        if (!s.isObject() ||
   1.469 +            (!(pid = xpc_JSObjectToID(cx, &s.toObject()))) ||
   1.470 +            (!(pid = (const nsID*) nsMemory::Clone(pid, sizeof(nsID))))) {
   1.471 +            return false;
   1.472 +        }
   1.473 +        *((const nsID**)d) = pid;
   1.474 +        return true;
   1.475 +    }
   1.476 +
   1.477 +    case nsXPTType::T_ASTRING:
   1.478 +    {
   1.479 +        if (JSVAL_IS_VOID(s)) {
   1.480 +            if (useAllocator)
   1.481 +                *((const nsAString**)d) = &NullString();
   1.482 +            else
   1.483 +                (**((nsAString**)d)).SetIsVoid(true);
   1.484 +            return true;
   1.485 +        }
   1.486 +        // Fall through to T_DOMSTRING case.
   1.487 +    }
   1.488 +    case nsXPTType::T_DOMSTRING:
   1.489 +    {
   1.490 +        if (JSVAL_IS_NULL(s)) {
   1.491 +            if (useAllocator)
   1.492 +                *((const nsAString**)d) = &NullString();
   1.493 +            else
   1.494 +                (**((nsAString**)d)).SetIsVoid(true);
   1.495 +            return true;
   1.496 +        }
   1.497 +        size_t length = 0;
   1.498 +        const char16_t* chars = nullptr;
   1.499 +        JSString* str = nullptr;
   1.500 +        if (!JSVAL_IS_VOID(s)) {
   1.501 +            str = ToString(cx, s);
   1.502 +            if (!str)
   1.503 +                return false;
   1.504 +
   1.505 +            chars = useAllocator ? JS_GetStringCharsZAndLength(cx, str, &length)
   1.506 +                                 : JS_GetStringCharsAndLength(cx, str, &length);
   1.507 +            if (!chars)
   1.508 +                return false;
   1.509 +
   1.510 +            if (!length) {
   1.511 +                if (useAllocator)
   1.512 +                    *((const nsAString**)d) = &EmptyString();
   1.513 +                else
   1.514 +                    (**((nsAString**)d)).Truncate();
   1.515 +                return true;
   1.516 +            }
   1.517 +        }
   1.518 +
   1.519 +        nsString* ws;
   1.520 +        if (useAllocator) {
   1.521 +            ws = nsXPConnect::GetRuntimeInstance()->NewShortLivedString();
   1.522 +            *((const nsString**)d) = ws;
   1.523 +        } else {
   1.524 +            ws = *((nsString**)d);
   1.525 +        }
   1.526 +
   1.527 +        if (!str) {
   1.528 +            ws->AssignLiteral(MOZ_UTF16("undefined"));
   1.529 +        } else if (XPCStringConvert::IsDOMString(str)) {
   1.530 +            // The characters represent an existing nsStringBuffer that
   1.531 +            // was shared by XPCStringConvert::ReadableToJSVal.
   1.532 +            nsStringBuffer::FromData((void *)chars)->ToString(length, *ws);
   1.533 +        } else if (XPCStringConvert::IsLiteral(str)) {
   1.534 +            // The characters represent a literal char16_t string constant
   1.535 +            // compiled into libxul, such as the string "undefined" above.
   1.536 +            ws->AssignLiteral(chars, length);
   1.537 +        } else if (useAllocator && STRING_TO_JSVAL(str) == s) {
   1.538 +            // The JS string will exist over the function call.
   1.539 +            // We don't need to copy the characters in this case.
   1.540 +            ws->Rebind(chars, length);
   1.541 +        } else {
   1.542 +            ws->Assign(chars, length);
   1.543 +        }
   1.544 +        return true;
   1.545 +    }
   1.546 +
   1.547 +    case nsXPTType::T_CHAR_STR:
   1.548 +    {
   1.549 +        if (JSVAL_IS_VOID(s) || JSVAL_IS_NULL(s)) {
   1.550 +            *((char**)d) = nullptr;
   1.551 +            return true;
   1.552 +        }
   1.553 +
   1.554 +        JSString* str = ToString(cx, s);
   1.555 +        if (!str) {
   1.556 +            return false;
   1.557 +        }
   1.558 +#ifdef DEBUG
   1.559 +        const jschar* chars=nullptr;
   1.560 +        if (nullptr != (chars = JS_GetStringCharsZ(cx, str))) {
   1.561 +            bool legalRange = true;
   1.562 +            int len = JS_GetStringLength(str);
   1.563 +            const jschar* t;
   1.564 +            int32_t i=0;
   1.565 +            for (t=chars; (i< len) && legalRange ; i++,t++) {
   1.566 +                if (!CheckJSCharInCharRange(*t))
   1.567 +                    break;
   1.568 +            }
   1.569 +        }
   1.570 +#endif // DEBUG
   1.571 +        size_t length = JS_GetStringEncodingLength(cx, str);
   1.572 +        if (length == size_t(-1)) {
   1.573 +            return false;
   1.574 +        }
   1.575 +        char *buffer = static_cast<char *>(nsMemory::Alloc(length + 1));
   1.576 +        if (!buffer) {
   1.577 +            return false;
   1.578 +        }
   1.579 +        JS_EncodeStringToBuffer(cx, str, buffer, length);
   1.580 +        buffer[length] = '\0';
   1.581 +        *((void**)d) = buffer;
   1.582 +        return true;
   1.583 +    }
   1.584 +
   1.585 +    case nsXPTType::T_WCHAR_STR:
   1.586 +    {
   1.587 +        const jschar* chars=nullptr;
   1.588 +        JSString* str;
   1.589 +
   1.590 +        if (JSVAL_IS_VOID(s) || JSVAL_IS_NULL(s)) {
   1.591 +            *((jschar**)d) = nullptr;
   1.592 +            return true;
   1.593 +        }
   1.594 +
   1.595 +        if (!(str = ToString(cx, s))) {
   1.596 +            return false;
   1.597 +        }
   1.598 +        if (!(chars = JS_GetStringCharsZ(cx, str))) {
   1.599 +            return false;
   1.600 +        }
   1.601 +        int len = JS_GetStringLength(str);
   1.602 +        int byte_len = (len+1)*sizeof(jschar);
   1.603 +        if (!(*((void**)d) = nsMemory::Alloc(byte_len))) {
   1.604 +            // XXX should report error
   1.605 +            return false;
   1.606 +        }
   1.607 +        jschar* destchars = *((jschar**)d);
   1.608 +        memcpy(destchars, chars, byte_len);
   1.609 +        destchars[len] = 0;
   1.610 +
   1.611 +        return true;
   1.612 +    }
   1.613 +
   1.614 +    case nsXPTType::T_UTF8STRING:
   1.615 +    {
   1.616 +        const jschar* chars;
   1.617 +        size_t length;
   1.618 +        JSString* str;
   1.619 +
   1.620 +        if (JSVAL_IS_NULL(s) || JSVAL_IS_VOID(s)) {
   1.621 +            if (useAllocator) {
   1.622 +                *((const nsACString**)d) = &NullCString();
   1.623 +            } else {
   1.624 +                nsCString* rs = *((nsCString**)d);
   1.625 +                rs->SetIsVoid(true);
   1.626 +            }
   1.627 +            return true;
   1.628 +        }
   1.629 +
   1.630 +        // The JS val is neither null nor void...
   1.631 +
   1.632 +        if (!(str = ToString(cx, s))||
   1.633 +            !(chars = JS_GetStringCharsAndLength(cx, str, &length))) {
   1.634 +            return false;
   1.635 +        }
   1.636 +
   1.637 +        if (!length) {
   1.638 +            if (useAllocator) {
   1.639 +                *((const nsACString**)d) = &EmptyCString();
   1.640 +            } else {
   1.641 +                nsCString* rs = *((nsCString**)d);
   1.642 +                rs->Truncate();
   1.643 +            }
   1.644 +            return true;
   1.645 +        }
   1.646 +
   1.647 +        nsCString *rs;
   1.648 +        if (useAllocator) {
   1.649 +            // Use nsCString to enable sharing
   1.650 +            rs = new nsCString();
   1.651 +            if (!rs)
   1.652 +                return false;
   1.653 +
   1.654 +            *((const nsCString**)d) = rs;
   1.655 +        } else {
   1.656 +            rs = *((nsCString**)d);
   1.657 +        }
   1.658 +        CopyUTF16toUTF8(Substring(chars, length), *rs);
   1.659 +        return true;
   1.660 +    }
   1.661 +
   1.662 +    case nsXPTType::T_CSTRING:
   1.663 +    {
   1.664 +        if (JSVAL_IS_NULL(s) || JSVAL_IS_VOID(s)) {
   1.665 +            if (useAllocator) {
   1.666 +                nsACString *rs = new nsCString();
   1.667 +                if (!rs)
   1.668 +                    return false;
   1.669 +
   1.670 +                rs->SetIsVoid(true);
   1.671 +                *((nsACString**)d) = rs;
   1.672 +            } else {
   1.673 +                nsACString* rs = *((nsACString**)d);
   1.674 +                rs->Truncate();
   1.675 +                rs->SetIsVoid(true);
   1.676 +            }
   1.677 +            return true;
   1.678 +        }
   1.679 +
   1.680 +        // The JS val is neither null nor void...
   1.681 +        JSString* str = ToString(cx, s);
   1.682 +        if (!str) {
   1.683 +            return false;
   1.684 +        }
   1.685 +
   1.686 +        size_t length = JS_GetStringEncodingLength(cx, str);
   1.687 +        if (length == size_t(-1)) {
   1.688 +            return false;
   1.689 +        }
   1.690 +
   1.691 +        if (!length) {
   1.692 +            if (useAllocator) {
   1.693 +                *((const nsACString**)d) = &EmptyCString();
   1.694 +            } else {
   1.695 +                nsCString* rs = *((nsCString**)d);
   1.696 +                rs->Truncate();
   1.697 +            }
   1.698 +            return true;
   1.699 +        }
   1.700 +
   1.701 +        nsACString *rs;
   1.702 +        if (useAllocator) {
   1.703 +            rs = new nsCString();
   1.704 +            if (!rs)
   1.705 +                return false;
   1.706 +            *((const nsACString**)d) = rs;
   1.707 +        } else {
   1.708 +            rs = *((nsACString**)d);
   1.709 +        }
   1.710 +
   1.711 +        rs->SetLength(uint32_t(length));
   1.712 +        if (rs->Length() != uint32_t(length)) {
   1.713 +            return false;
   1.714 +        }
   1.715 +        JS_EncodeStringToBuffer(cx, str, rs->BeginWriting(), length);
   1.716 +
   1.717 +        return true;
   1.718 +    }
   1.719 +
   1.720 +    case nsXPTType::T_INTERFACE:
   1.721 +    case nsXPTType::T_INTERFACE_IS:
   1.722 +    {
   1.723 +        MOZ_ASSERT(iid,"can't do interface conversions without iid");
   1.724 +
   1.725 +        if (iid->Equals(NS_GET_IID(nsIVariant))) {
   1.726 +            nsCOMPtr<nsIVariant> variant = XPCVariant::newVariant(cx, s);
   1.727 +            if (!variant)
   1.728 +                return false;
   1.729 +
   1.730 +            variant.forget(static_cast<nsISupports**>(d));
   1.731 +            return true;
   1.732 +        } else if (iid->Equals(NS_GET_IID(nsIAtom)) &&
   1.733 +                   JSVAL_IS_STRING(s)) {
   1.734 +            // We're trying to pass a string as an nsIAtom.  Let's atomize!
   1.735 +            JSString* str = JSVAL_TO_STRING(s);
   1.736 +            const char16_t* chars = JS_GetStringCharsZ(cx, str);
   1.737 +            if (!chars) {
   1.738 +                if (pErr)
   1.739 +                    *pErr = NS_ERROR_XPC_BAD_CONVERT_JS_NULL_REF;
   1.740 +                return false;
   1.741 +            }
   1.742 +            uint32_t length = JS_GetStringLength(str);
   1.743 +            nsCOMPtr<nsIAtom> atom =
   1.744 +                NS_NewAtom(nsDependentSubstring(chars, chars + length));
   1.745 +            atom.forget((nsISupports**)d);
   1.746 +            return true;
   1.747 +        }
   1.748 +        //else ...
   1.749 +
   1.750 +        if (s.isNullOrUndefined()) {
   1.751 +            *((nsISupports**)d) = nullptr;
   1.752 +            return true;
   1.753 +        }
   1.754 +
   1.755 +        // only wrap JSObjects
   1.756 +        if (!s.isObject()) {
   1.757 +            if (pErr && s.isInt32() && 0 == s.toInt32())
   1.758 +                *pErr = NS_ERROR_XPC_BAD_CONVERT_JS_ZERO_ISNOT_NULL;
   1.759 +            return false;
   1.760 +        }
   1.761 +
   1.762 +        RootedObject src(cx, &s.toObject());
   1.763 +        return JSObject2NativeInterface((void**)d, src, iid, nullptr, pErr);
   1.764 +    }
   1.765 +    default:
   1.766 +        NS_ERROR("bad type");
   1.767 +        return false;
   1.768 +    }
   1.769 +    return true;
   1.770 +}
   1.771 +
   1.772 +static inline bool
   1.773 +CreateHolderIfNeeded(HandleObject obj, MutableHandleValue d,
   1.774 +                     nsIXPConnectJSObjectHolder** dest)
   1.775 +{
   1.776 +    if (dest) {
   1.777 +        nsRefPtr<XPCJSObjectHolder> objHolder = XPCJSObjectHolder::newHolder(obj);
   1.778 +        if (!objHolder)
   1.779 +            return false;
   1.780 +
   1.781 +        objHolder.forget(dest);
   1.782 +    }
   1.783 +
   1.784 +    d.setObjectOrNull(obj);
   1.785 +
   1.786 +    return true;
   1.787 +}
   1.788 +
   1.789 +/***************************************************************************/
   1.790 +// static
   1.791 +bool
   1.792 +XPCConvert::NativeInterface2JSObject(MutableHandleValue d,
   1.793 +                                     nsIXPConnectJSObjectHolder** dest,
   1.794 +                                     xpcObjectHelper& aHelper,
   1.795 +                                     const nsID* iid,
   1.796 +                                     XPCNativeInterface** Interface,
   1.797 +                                     bool allowNativeWrapper,
   1.798 +                                     nsresult* pErr)
   1.799 +{
   1.800 +    MOZ_ASSERT_IF(Interface, iid);
   1.801 +    if (!iid)
   1.802 +        iid = &NS_GET_IID(nsISupports);
   1.803 +
   1.804 +    d.setNull();
   1.805 +    if (dest)
   1.806 +        *dest = nullptr;
   1.807 +    if (!aHelper.Object())
   1.808 +        return true;
   1.809 +    if (pErr)
   1.810 +        *pErr = NS_ERROR_XPC_BAD_CONVERT_NATIVE;
   1.811 +
   1.812 +    // We used to have code here that unwrapped and simply exposed the
   1.813 +    // underlying JSObject. That caused anomolies when JSComponents were
   1.814 +    // accessed from other JS code - they didn't act like other xpconnect
   1.815 +    // wrapped components. So, instead, we create "double wrapped" objects
   1.816 +    // (that means an XPCWrappedNative around an nsXPCWrappedJS). This isn't
   1.817 +    // optimal -- we could detect this and roll the functionality into a
   1.818 +    // single wrapper, but the current solution is good enough for now.
   1.819 +    AutoJSContext cx;
   1.820 +    XPCWrappedNativeScope* xpcscope = GetObjectScope(JS::CurrentGlobalOrNull(cx));
   1.821 +    if (!xpcscope)
   1.822 +        return false;
   1.823 +
   1.824 +    // First, see if this object supports the wrapper cache.
   1.825 +    // Note: If |cache->IsDOMBinding()| is true, then it means that the object
   1.826 +    // implementing it doesn't want a wrapped native as its JS Object, but
   1.827 +    // instead it provides its own proxy object. In that case, the object
   1.828 +    // to use is found as cache->GetWrapper(). If that is null, then the
   1.829 +    // object will create (and fill the cache) from its WrapObject call.
   1.830 +    nsWrapperCache *cache = aHelper.GetWrapperCache();
   1.831 +
   1.832 +    RootedObject flat(cx, cache ? cache->GetWrapper() : nullptr);
   1.833 +    if (!flat && cache && cache->IsDOMBinding()) {
   1.834 +        RootedObject global(cx, xpcscope->GetGlobalJSObject());
   1.835 +        js::AssertSameCompartment(cx, global);
   1.836 +        flat = cache->WrapObject(cx);
   1.837 +        if (!flat)
   1.838 +            return false;
   1.839 +    }
   1.840 +    if (flat) {
   1.841 +        if (allowNativeWrapper && !JS_WrapObject(cx, &flat))
   1.842 +            return false;
   1.843 +        return CreateHolderIfNeeded(flat, d, dest);
   1.844 +    }
   1.845 +
   1.846 +    // Don't double wrap CPOWs. This is a temporary measure for compatibility
   1.847 +    // with objects that don't provide necessary QIs (such as objects under
   1.848 +    // the new DOM bindings). We expect the other side of the CPOW to have
   1.849 +    // the appropriate wrappers in place.
   1.850 +    RootedObject cpow(cx, UnwrapNativeCPOW(aHelper.Object()));
   1.851 +    if (cpow) {
   1.852 +        if (!JS_WrapObject(cx, &cpow))
   1.853 +            return false;
   1.854 +        d.setObject(*cpow);
   1.855 +        return true;
   1.856 +    }
   1.857 +
   1.858 +    // We can't simply construct a slim wrapper. Go ahead and create an
   1.859 +    // XPCWrappedNative for this object. At this point, |flat| could be
   1.860 +    // non-null, meaning that either we already have a wrapped native from
   1.861 +    // the cache (which might need to be QI'd to the new interface) or that
   1.862 +    // we found a slim wrapper that we'll have to morph.
   1.863 +    AutoMarkingNativeInterfacePtr iface(cx);
   1.864 +    if (iid) {
   1.865 +        if (Interface)
   1.866 +            iface = *Interface;
   1.867 +
   1.868 +        if (!iface) {
   1.869 +            iface = XPCNativeInterface::GetNewOrUsed(iid);
   1.870 +            if (!iface)
   1.871 +                return false;
   1.872 +
   1.873 +            if (Interface)
   1.874 +                *Interface = iface;
   1.875 +        }
   1.876 +    }
   1.877 +
   1.878 +    MOZ_ASSERT(!flat || IS_WN_REFLECTOR(flat), "What kind of wrapper is this?");
   1.879 +
   1.880 +    nsresult rv;
   1.881 +    XPCWrappedNative* wrapper;
   1.882 +    nsRefPtr<XPCWrappedNative> strongWrapper;
   1.883 +    if (!flat) {
   1.884 +        rv = XPCWrappedNative::GetNewOrUsed(aHelper, xpcscope, iface,
   1.885 +                                            getter_AddRefs(strongWrapper));
   1.886 +
   1.887 +        wrapper = strongWrapper;
   1.888 +    } else {
   1.889 +        MOZ_ASSERT(IS_WN_REFLECTOR(flat));
   1.890 +
   1.891 +        wrapper = XPCWrappedNative::Get(flat);
   1.892 +
   1.893 +        // If asked to return the wrapper we'll return a strong reference,
   1.894 +        // otherwise we'll just return its JSObject in d (which should be
   1.895 +        // rooted in that case).
   1.896 +        if (dest)
   1.897 +            strongWrapper = wrapper;
   1.898 +        if (iface)
   1.899 +            wrapper->FindTearOff(iface, false, &rv);
   1.900 +        else
   1.901 +            rv = NS_OK;
   1.902 +    }
   1.903 +
   1.904 +    if (NS_FAILED(rv) && pErr)
   1.905 +        *pErr = rv;
   1.906 +
   1.907 +    // If creating the wrapped native failed, then return early.
   1.908 +    if (NS_FAILED(rv) || !wrapper)
   1.909 +        return false;
   1.910 +
   1.911 +    // If we're not creating security wrappers, we can return the
   1.912 +    // XPCWrappedNative as-is here.
   1.913 +    flat = wrapper->GetFlatJSObject();
   1.914 +    jsval v = OBJECT_TO_JSVAL(flat);
   1.915 +    if (!allowNativeWrapper) {
   1.916 +        d.set(v);
   1.917 +        if (dest)
   1.918 +            strongWrapper.forget(dest);
   1.919 +        if (pErr)
   1.920 +            *pErr = NS_OK;
   1.921 +        return true;
   1.922 +    }
   1.923 +
   1.924 +    // The call to wrap here handles both cross-compartment and same-compartment
   1.925 +    // security wrappers.
   1.926 +    RootedObject original(cx, flat);
   1.927 +    if (!JS_WrapObject(cx, &flat))
   1.928 +        return false;
   1.929 +
   1.930 +    d.setObjectOrNull(flat);
   1.931 +
   1.932 +    if (dest) {
   1.933 +        // The strongWrapper still holds the original flat object.
   1.934 +        if (flat == original) {
   1.935 +            strongWrapper.forget(dest);
   1.936 +        } else {
   1.937 +            nsRefPtr<XPCJSObjectHolder> objHolder =
   1.938 +                XPCJSObjectHolder::newHolder(flat);
   1.939 +            if (!objHolder)
   1.940 +                return false;
   1.941 +
   1.942 +            objHolder.forget(dest);
   1.943 +        }
   1.944 +    }
   1.945 +
   1.946 +    if (pErr)
   1.947 +        *pErr = NS_OK;
   1.948 +
   1.949 +    return true;
   1.950 +}
   1.951 +
   1.952 +/***************************************************************************/
   1.953 +
   1.954 +// static
   1.955 +bool
   1.956 +XPCConvert::JSObject2NativeInterface(void** dest, HandleObject src,
   1.957 +                                     const nsID* iid,
   1.958 +                                     nsISupports* aOuter,
   1.959 +                                     nsresult* pErr)
   1.960 +{
   1.961 +    MOZ_ASSERT(dest, "bad param");
   1.962 +    MOZ_ASSERT(src, "bad param");
   1.963 +    MOZ_ASSERT(iid, "bad param");
   1.964 +
   1.965 +    AutoJSContext cx;
   1.966 +    JSAutoCompartment ac(cx, src);
   1.967 +
   1.968 +    *dest = nullptr;
   1.969 +     if (pErr)
   1.970 +        *pErr = NS_ERROR_XPC_BAD_CONVERT_JS;
   1.971 +
   1.972 +    nsISupports* iface;
   1.973 +
   1.974 +    if (!aOuter) {
   1.975 +        // Note that if we have a non-null aOuter then it means that we are
   1.976 +        // forcing the creation of a wrapper even if the object *is* a
   1.977 +        // wrappedNative or other wise has 'nsISupportness'.
   1.978 +        // This allows wrapJSAggregatedToNative to work.
   1.979 +
   1.980 +        // If we're looking at a security wrapper, see now if we're allowed to
   1.981 +        // pass it to C++. If we are, then fall through to the code below. If
   1.982 +        // we aren't, throw an exception eagerly.
   1.983 +        //
   1.984 +        // NB: It's very important that we _don't_ unwrap in the aOuter case,
   1.985 +        // because the caller may explicitly want to create the XPCWrappedJS
   1.986 +        // around a security wrapper. XBL does this with Xrays from the XBL
   1.987 +        // scope - see nsBindingManager::GetBindingImplementation.
   1.988 +        JSObject* inner = js::CheckedUnwrap(src, /* stopAtOuter = */ false);
   1.989 +
   1.990 +        // Hack - For historical reasons, wrapped chrome JS objects have been
   1.991 +        // passable as native interfaces. We'd like to fix this, but it
   1.992 +        // involves fixing the contacts API and PeerConnection to stop using
   1.993 +        // COWs. This needs to happen, but for now just preserve the old
   1.994 +        // behavior.
   1.995 +        //
   1.996 +        // Note that there is an identical hack in getWrapper which should be
   1.997 +        // removed if this one is.
   1.998 +        if (!inner && MOZ_UNLIKELY(xpc::WrapperFactory::IsCOW(src)))
   1.999 +            inner = js::UncheckedUnwrap(src);
  1.1000 +        if (!inner) {
  1.1001 +            if (pErr)
  1.1002 +                *pErr = NS_ERROR_XPC_SECURITY_MANAGER_VETO;
  1.1003 +            return false;
  1.1004 +        }
  1.1005 +
  1.1006 +        // Is this really a native xpcom object with a wrapper?
  1.1007 +        XPCWrappedNative* wrappedNative = nullptr;
  1.1008 +        if (IS_WN_REFLECTOR(inner))
  1.1009 +            wrappedNative = XPCWrappedNative::Get(inner);
  1.1010 +        if (wrappedNative) {
  1.1011 +            iface = wrappedNative->GetIdentityObject();
  1.1012 +            return NS_SUCCEEDED(iface->QueryInterface(*iid, dest));
  1.1013 +        }
  1.1014 +        // else...
  1.1015 +
  1.1016 +        // Deal with slim wrappers here.
  1.1017 +        if (GetISupportsFromJSObject(inner ? inner : src, &iface)) {
  1.1018 +            if (iface)
  1.1019 +                return NS_SUCCEEDED(iface->QueryInterface(*iid, dest));
  1.1020 +
  1.1021 +            return false;
  1.1022 +        }
  1.1023 +    }
  1.1024 +
  1.1025 +    // else...
  1.1026 +
  1.1027 +    nsXPCWrappedJS* wrapper;
  1.1028 +    nsresult rv = nsXPCWrappedJS::GetNewOrUsed(src, *iid, &wrapper);
  1.1029 +    if (pErr)
  1.1030 +        *pErr = rv;
  1.1031 +    if (NS_SUCCEEDED(rv) && wrapper) {
  1.1032 +        // If the caller wanted to aggregate this JS object to a native,
  1.1033 +        // attach it to the wrapper. Note that we allow a maximum of one
  1.1034 +        // aggregated native for a given XPCWrappedJS.
  1.1035 +        if (aOuter)
  1.1036 +            wrapper->SetAggregatedNativeObject(aOuter);
  1.1037 +
  1.1038 +        // We need to go through the QueryInterface logic to make this return
  1.1039 +        // the right thing for the various 'special' interfaces; e.g.
  1.1040 +        // nsIPropertyBag. We must use AggregatedQueryInterface in cases where
  1.1041 +        // there is an outer to avoid nasty recursion.
  1.1042 +        rv = aOuter ? wrapper->AggregatedQueryInterface(*iid, dest) :
  1.1043 +                      wrapper->QueryInterface(*iid, dest);
  1.1044 +        if (pErr)
  1.1045 +            *pErr = rv;
  1.1046 +        NS_RELEASE(wrapper);
  1.1047 +        return NS_SUCCEEDED(rv);
  1.1048 +    }
  1.1049 +
  1.1050 +    // else...
  1.1051 +    return false;
  1.1052 +}
  1.1053 +
  1.1054 +/***************************************************************************/
  1.1055 +/***************************************************************************/
  1.1056 +
  1.1057 +// static
  1.1058 +nsresult
  1.1059 +XPCConvert::ConstructException(nsresult rv, const char* message,
  1.1060 +                               const char* ifaceName, const char* methodName,
  1.1061 +                               nsISupports* data,
  1.1062 +                               nsIException** exceptn,
  1.1063 +                               JSContext* cx,
  1.1064 +                               jsval* jsExceptionPtr)
  1.1065 +{
  1.1066 +    MOZ_ASSERT(!cx == !jsExceptionPtr, "Expected cx and jsExceptionPtr to cooccur.");
  1.1067 +
  1.1068 +    static const char format[] = "\'%s\' when calling method: [%s::%s]";
  1.1069 +    const char * msg = message;
  1.1070 +    nsXPIDLString xmsg;
  1.1071 +    nsAutoCString sxmsg;
  1.1072 +
  1.1073 +    nsCOMPtr<nsIScriptError> errorObject = do_QueryInterface(data);
  1.1074 +    if (errorObject) {
  1.1075 +        if (NS_SUCCEEDED(errorObject->GetMessageMoz(getter_Copies(xmsg)))) {
  1.1076 +            CopyUTF16toUTF8(xmsg, sxmsg);
  1.1077 +            msg = sxmsg.get();
  1.1078 +        }
  1.1079 +    }
  1.1080 +    if (!msg)
  1.1081 +        if (!nsXPCException::NameAndFormatForNSResult(rv, nullptr, &msg) || ! msg)
  1.1082 +            msg = "<error>";
  1.1083 +
  1.1084 +    nsCString msgStr(msg);
  1.1085 +    if (ifaceName && methodName)
  1.1086 +        msgStr.AppendPrintf(format, msg, ifaceName, methodName);
  1.1087 +
  1.1088 +    nsRefPtr<Exception> e = new Exception(msgStr, rv, EmptyCString(), nullptr, data);
  1.1089 +
  1.1090 +    if (cx && jsExceptionPtr) {
  1.1091 +        e->StowJSVal(*jsExceptionPtr);
  1.1092 +    }
  1.1093 +
  1.1094 +    e.forget(exceptn);
  1.1095 +    return NS_OK;
  1.1096 +}
  1.1097 +
  1.1098 +/********************************/
  1.1099 +
  1.1100 +class MOZ_STACK_CLASS AutoExceptionRestorer
  1.1101 +{
  1.1102 +public:
  1.1103 +    AutoExceptionRestorer(JSContext *cx, Value v)
  1.1104 +        : mContext(cx), tvr(cx, v)
  1.1105 +    {
  1.1106 +        JS_ClearPendingException(mContext);
  1.1107 +    }
  1.1108 +
  1.1109 +    ~AutoExceptionRestorer()
  1.1110 +    {
  1.1111 +        JS_SetPendingException(mContext, tvr);
  1.1112 +    }
  1.1113 +
  1.1114 +private:
  1.1115 +    JSContext * const mContext;
  1.1116 +    RootedValue tvr;
  1.1117 +};
  1.1118 +
  1.1119 +// static
  1.1120 +nsresult
  1.1121 +XPCConvert::JSValToXPCException(MutableHandleValue s,
  1.1122 +                                const char* ifaceName,
  1.1123 +                                const char* methodName,
  1.1124 +                                nsIException** exceptn)
  1.1125 +{
  1.1126 +    AutoJSContext cx;
  1.1127 +    AutoExceptionRestorer aer(cx, s);
  1.1128 +
  1.1129 +    if (!JSVAL_IS_PRIMITIVE(s)) {
  1.1130 +        // we have a JSObject
  1.1131 +        RootedObject obj(cx, JSVAL_TO_OBJECT(s));
  1.1132 +
  1.1133 +        if (!obj) {
  1.1134 +            NS_ERROR("when is an object not an object?");
  1.1135 +            return NS_ERROR_FAILURE;
  1.1136 +        }
  1.1137 +
  1.1138 +        // is this really a native xpcom object with a wrapper?
  1.1139 +        JSObject *unwrapped = js::CheckedUnwrap(obj, /* stopAtOuter = */ false);
  1.1140 +        if (!unwrapped)
  1.1141 +            return NS_ERROR_XPC_SECURITY_MANAGER_VETO;
  1.1142 +        XPCWrappedNative* wrapper = IS_WN_REFLECTOR(unwrapped) ? XPCWrappedNative::Get(unwrapped)
  1.1143 +                                                               : nullptr;
  1.1144 +        if (wrapper) {
  1.1145 +            nsISupports* supports = wrapper->GetIdentityObject();
  1.1146 +            nsCOMPtr<nsIException> iface = do_QueryInterface(supports);
  1.1147 +            if (iface) {
  1.1148 +                // just pass through the exception (with extra ref and all)
  1.1149 +                nsCOMPtr<nsIException> temp = iface;
  1.1150 +                temp.forget(exceptn);
  1.1151 +                return NS_OK;
  1.1152 +            } else {
  1.1153 +                // it is a wrapped native, but not an exception!
  1.1154 +                return ConstructException(NS_ERROR_XPC_JS_THREW_NATIVE_OBJECT,
  1.1155 +                                          nullptr, ifaceName, methodName, supports,
  1.1156 +                                          exceptn, nullptr, nullptr);
  1.1157 +            }
  1.1158 +        } else {
  1.1159 +            // It is a JSObject, but not a wrapped native...
  1.1160 +
  1.1161 +            // If it is an engine Error with an error report then let's
  1.1162 +            // extract the report and build an xpcexception from that
  1.1163 +            const JSErrorReport* report;
  1.1164 +            if (nullptr != (report = JS_ErrorFromException(cx, obj))) {
  1.1165 +                JSAutoByteString message;
  1.1166 +                JSString* str;
  1.1167 +                if (nullptr != (str = ToString(cx, s)))
  1.1168 +                    message.encodeLatin1(cx, str);
  1.1169 +                return JSErrorToXPCException(message.ptr(), ifaceName,
  1.1170 +                                             methodName, report, exceptn);
  1.1171 +            }
  1.1172 +
  1.1173 +
  1.1174 +            bool found;
  1.1175 +
  1.1176 +            // heuristic to see if it might be usable as an xpcexception
  1.1177 +            if (!JS_HasProperty(cx, obj, "message", &found))
  1.1178 +                return NS_ERROR_FAILURE;
  1.1179 +
  1.1180 +            if (found && !JS_HasProperty(cx, obj, "result", &found))
  1.1181 +                return NS_ERROR_FAILURE;
  1.1182 +
  1.1183 +            if (found) {
  1.1184 +                // lets try to build a wrapper around the JSObject
  1.1185 +                nsXPCWrappedJS* jswrapper;
  1.1186 +                nsresult rv =
  1.1187 +                    nsXPCWrappedJS::GetNewOrUsed(obj, NS_GET_IID(nsIException), &jswrapper);
  1.1188 +                if (NS_FAILED(rv))
  1.1189 +                    return rv;
  1.1190 +
  1.1191 +                *exceptn = static_cast<nsIException *>(jswrapper->GetXPTCStub());
  1.1192 +                return NS_OK;
  1.1193 +            }
  1.1194 +
  1.1195 +
  1.1196 +            // XXX we should do a check against 'js_ErrorClass' here and
  1.1197 +            // do the right thing - even though it has no JSErrorReport,
  1.1198 +            // The fact that it is a JSError exceptions means we can extract
  1.1199 +            // particular info and our 'result' should reflect that.
  1.1200 +
  1.1201 +            // otherwise we'll just try to convert it to a string
  1.1202 +
  1.1203 +            JSString* str = ToString(cx, s);
  1.1204 +            if (!str)
  1.1205 +                return NS_ERROR_FAILURE;
  1.1206 +
  1.1207 +            JSAutoByteString strBytes(cx, str);
  1.1208 +            if (!strBytes)
  1.1209 +                return NS_ERROR_FAILURE;
  1.1210 +
  1.1211 +            return ConstructException(NS_ERROR_XPC_JS_THREW_JS_OBJECT,
  1.1212 +                                      strBytes.ptr(), ifaceName, methodName,
  1.1213 +                                      nullptr, exceptn, cx, s.address());
  1.1214 +        }
  1.1215 +    }
  1.1216 +
  1.1217 +    if (JSVAL_IS_VOID(s) || JSVAL_IS_NULL(s)) {
  1.1218 +        return ConstructException(NS_ERROR_XPC_JS_THREW_NULL,
  1.1219 +                                  nullptr, ifaceName, methodName, nullptr,
  1.1220 +                                  exceptn, cx, s.address());
  1.1221 +    }
  1.1222 +
  1.1223 +    if (JSVAL_IS_NUMBER(s)) {
  1.1224 +        // lets see if it looks like an nsresult
  1.1225 +        nsresult rv;
  1.1226 +        double number;
  1.1227 +        bool isResult = false;
  1.1228 +
  1.1229 +        if (JSVAL_IS_INT(s)) {
  1.1230 +            rv = (nsresult) JSVAL_TO_INT(s);
  1.1231 +            if (NS_FAILED(rv))
  1.1232 +                isResult = true;
  1.1233 +            else
  1.1234 +                number = (double) JSVAL_TO_INT(s);
  1.1235 +        } else {
  1.1236 +            number = JSVAL_TO_DOUBLE(s);
  1.1237 +            if (number > 0.0 &&
  1.1238 +                number < (double)0xffffffff &&
  1.1239 +                0.0 == fmod(number,1)) {
  1.1240 +                // Visual Studio 9 doesn't allow casting directly from a
  1.1241 +                // double to an enumeration type, contrary to 5.2.9(10) of
  1.1242 +                // C++11, so add an intermediate cast.
  1.1243 +                rv = (nsresult)(uint32_t) number;
  1.1244 +                if (NS_FAILED(rv))
  1.1245 +                    isResult = true;
  1.1246 +            }
  1.1247 +        }
  1.1248 +
  1.1249 +        if (isResult)
  1.1250 +            return ConstructException(rv, nullptr, ifaceName, methodName,
  1.1251 +                                      nullptr, exceptn, cx, s.address());
  1.1252 +        else {
  1.1253 +            // XXX all this nsISupportsDouble code seems a little redundant
  1.1254 +            // now that we're storing the jsval in the exception...
  1.1255 +            nsISupportsDouble* data;
  1.1256 +            nsCOMPtr<nsIComponentManager> cm;
  1.1257 +            if (NS_FAILED(NS_GetComponentManager(getter_AddRefs(cm))) || !cm ||
  1.1258 +                NS_FAILED(cm->CreateInstanceByContractID(NS_SUPPORTS_DOUBLE_CONTRACTID,
  1.1259 +                                                         nullptr,
  1.1260 +                                                         NS_GET_IID(nsISupportsDouble),
  1.1261 +                                                         (void**)&data)))
  1.1262 +                return NS_ERROR_FAILURE;
  1.1263 +            data->SetData(number);
  1.1264 +            rv = ConstructException(NS_ERROR_XPC_JS_THREW_NUMBER, nullptr,
  1.1265 +                                    ifaceName, methodName, data, exceptn, cx, s.address());
  1.1266 +            NS_RELEASE(data);
  1.1267 +            return rv;
  1.1268 +        }
  1.1269 +    }
  1.1270 +
  1.1271 +    // otherwise we'll just try to convert it to a string
  1.1272 +    // Note: e.g., bools get converted to JSStrings by this code.
  1.1273 +
  1.1274 +    JSString* str = ToString(cx, s);
  1.1275 +    if (str) {
  1.1276 +        JSAutoByteString strBytes(cx, str);
  1.1277 +        if (!!strBytes) {
  1.1278 +            return ConstructException(NS_ERROR_XPC_JS_THREW_STRING,
  1.1279 +                                      strBytes.ptr(), ifaceName, methodName,
  1.1280 +                                      nullptr, exceptn, cx, s.address());
  1.1281 +        }
  1.1282 +    }
  1.1283 +    return NS_ERROR_FAILURE;
  1.1284 +}
  1.1285 +
  1.1286 +/********************************/
  1.1287 +
  1.1288 +// static
  1.1289 +nsresult
  1.1290 +XPCConvert::JSErrorToXPCException(const char* message,
  1.1291 +                                  const char* ifaceName,
  1.1292 +                                  const char* methodName,
  1.1293 +                                  const JSErrorReport* report,
  1.1294 +                                  nsIException** exceptn)
  1.1295 +{
  1.1296 +    AutoJSContext cx;
  1.1297 +    nsresult rv = NS_ERROR_FAILURE;
  1.1298 +    nsRefPtr<nsScriptError> data;
  1.1299 +    if (report) {
  1.1300 +        nsAutoString bestMessage;
  1.1301 +        if (report && report->ucmessage) {
  1.1302 +            bestMessage = static_cast<const char16_t*>(report->ucmessage);
  1.1303 +        } else if (message) {
  1.1304 +            CopyASCIItoUTF16(message, bestMessage);
  1.1305 +        } else {
  1.1306 +            bestMessage.AssignLiteral("JavaScript Error");
  1.1307 +        }
  1.1308 +
  1.1309 +        const char16_t* uclinebuf =
  1.1310 +            static_cast<const char16_t*>(report->uclinebuf);
  1.1311 +
  1.1312 +        data = new nsScriptError();
  1.1313 +        data->InitWithWindowID(
  1.1314 +            bestMessage,
  1.1315 +            NS_ConvertASCIItoUTF16(report->filename),
  1.1316 +            uclinebuf ? nsDependentString(uclinebuf) : EmptyString(),
  1.1317 +            report->lineno,
  1.1318 +            report->uctokenptr - report->uclinebuf, report->flags,
  1.1319 +            NS_LITERAL_CSTRING("XPConnect JavaScript"),
  1.1320 +            nsJSUtils::GetCurrentlyRunningCodeInnerWindowID(cx));
  1.1321 +    }
  1.1322 +
  1.1323 +    if (data) {
  1.1324 +        nsAutoCString formattedMsg;
  1.1325 +        data->ToString(formattedMsg);
  1.1326 +
  1.1327 +        rv = ConstructException(NS_ERROR_XPC_JAVASCRIPT_ERROR_WITH_DETAILS,
  1.1328 +                                formattedMsg.get(), ifaceName, methodName,
  1.1329 +                                static_cast<nsIScriptError*>(data.get()),
  1.1330 +                                exceptn, nullptr, nullptr);
  1.1331 +    } else {
  1.1332 +        rv = ConstructException(NS_ERROR_XPC_JAVASCRIPT_ERROR,
  1.1333 +                                nullptr, ifaceName, methodName, nullptr,
  1.1334 +                                exceptn, nullptr, nullptr);
  1.1335 +    }
  1.1336 +    return rv;
  1.1337 +}
  1.1338 +
  1.1339 +/***************************************************************************/
  1.1340 +
  1.1341 +// array fun...
  1.1342 +
  1.1343 +#ifdef POPULATE
  1.1344 +#undef POPULATE
  1.1345 +#endif
  1.1346 +
  1.1347 +// static
  1.1348 +bool
  1.1349 +XPCConvert::NativeArray2JS(MutableHandleValue d, const void** s,
  1.1350 +                           const nsXPTType& type, const nsID* iid,
  1.1351 +                           uint32_t count, nsresult* pErr)
  1.1352 +{
  1.1353 +    NS_PRECONDITION(s, "bad param");
  1.1354 +
  1.1355 +    AutoJSContext cx;
  1.1356 +
  1.1357 +    // XXX add support for putting chars in a string rather than an array
  1.1358 +
  1.1359 +    // XXX add support to indicate *which* array element was not convertable
  1.1360 +
  1.1361 +    RootedObject array(cx, JS_NewArrayObject(cx, count));
  1.1362 +    if (!array)
  1.1363 +        return false;
  1.1364 +
  1.1365 +    if (pErr)
  1.1366 +        *pErr = NS_ERROR_XPC_BAD_CONVERT_NATIVE;
  1.1367 +
  1.1368 +    uint32_t i;
  1.1369 +    RootedValue current(cx, JSVAL_NULL);
  1.1370 +
  1.1371 +#define POPULATE(_t)                                                                    \
  1.1372 +    PR_BEGIN_MACRO                                                                      \
  1.1373 +        for (i = 0; i < count; i++) {                                                   \
  1.1374 +            if (!NativeData2JS(&current, ((_t*)*s)+i, type, iid, pErr) ||               \
  1.1375 +                !JS_SetElement(cx, array, i, current))                                  \
  1.1376 +                goto failure;                                                           \
  1.1377 +        }                                                                               \
  1.1378 +    PR_END_MACRO
  1.1379 +
  1.1380 +    // XXX check IsPtr - esp. to handle array of nsID (as opposed to nsID*)
  1.1381 +
  1.1382 +    switch (type.TagPart()) {
  1.1383 +    case nsXPTType::T_I8            : POPULATE(int8_t);         break;
  1.1384 +    case nsXPTType::T_I16           : POPULATE(int16_t);        break;
  1.1385 +    case nsXPTType::T_I32           : POPULATE(int32_t);        break;
  1.1386 +    case nsXPTType::T_I64           : POPULATE(int64_t);        break;
  1.1387 +    case nsXPTType::T_U8            : POPULATE(uint8_t);        break;
  1.1388 +    case nsXPTType::T_U16           : POPULATE(uint16_t);       break;
  1.1389 +    case nsXPTType::T_U32           : POPULATE(uint32_t);       break;
  1.1390 +    case nsXPTType::T_U64           : POPULATE(uint64_t);       break;
  1.1391 +    case nsXPTType::T_FLOAT         : POPULATE(float);          break;
  1.1392 +    case nsXPTType::T_DOUBLE        : POPULATE(double);         break;
  1.1393 +    case nsXPTType::T_BOOL          : POPULATE(bool);           break;
  1.1394 +    case nsXPTType::T_CHAR          : POPULATE(char);           break;
  1.1395 +    case nsXPTType::T_WCHAR         : POPULATE(jschar);         break;
  1.1396 +    case nsXPTType::T_VOID          : NS_ERROR("bad type"); goto failure;
  1.1397 +    case nsXPTType::T_IID           : POPULATE(nsID*);          break;
  1.1398 +    case nsXPTType::T_DOMSTRING     : NS_ERROR("bad type"); goto failure;
  1.1399 +    case nsXPTType::T_CHAR_STR      : POPULATE(char*);          break;
  1.1400 +    case nsXPTType::T_WCHAR_STR     : POPULATE(jschar*);        break;
  1.1401 +    case nsXPTType::T_INTERFACE     : POPULATE(nsISupports*);   break;
  1.1402 +    case nsXPTType::T_INTERFACE_IS  : POPULATE(nsISupports*);   break;
  1.1403 +    case nsXPTType::T_UTF8STRING    : NS_ERROR("bad type"); goto failure;
  1.1404 +    case nsXPTType::T_CSTRING       : NS_ERROR("bad type"); goto failure;
  1.1405 +    case nsXPTType::T_ASTRING       : NS_ERROR("bad type"); goto failure;
  1.1406 +    default                         : NS_ERROR("bad type"); goto failure;
  1.1407 +    }
  1.1408 +
  1.1409 +    if (pErr)
  1.1410 +        *pErr = NS_OK;
  1.1411 +    d.setObject(*array);
  1.1412 +    return true;
  1.1413 +
  1.1414 +failure:
  1.1415 +    return false;
  1.1416 +
  1.1417 +#undef POPULATE
  1.1418 +}
  1.1419 +
  1.1420 +
  1.1421 +
  1.1422 +// Check that the tag part of the type matches the type
  1.1423 +// of the array. If the check succeeds, check that the size
  1.1424 +// of the output does not exceed UINT32_MAX bytes. Allocate
  1.1425 +// the memory and copy the elements by memcpy.
  1.1426 +static bool
  1.1427 +CheckTargetAndPopulate(const nsXPTType& type,
  1.1428 +                       uint8_t requiredType,
  1.1429 +                       size_t typeSize,
  1.1430 +                       uint32_t count,
  1.1431 +                       JSObject* tArr,
  1.1432 +                       void** output,
  1.1433 +                       nsresult* pErr)
  1.1434 +{
  1.1435 +    // Check that the element type expected by the interface matches
  1.1436 +    // the type of the elements in the typed array exactly, including
  1.1437 +    // signedness.
  1.1438 +    if (type.TagPart() != requiredType) {
  1.1439 +        if (pErr)
  1.1440 +            *pErr = NS_ERROR_XPC_BAD_CONVERT_JS;
  1.1441 +
  1.1442 +        return false;
  1.1443 +    }
  1.1444 +
  1.1445 +    // Calulate the maximum number of elements that can fit in
  1.1446 +    // UINT32_MAX bytes.
  1.1447 +    size_t max = UINT32_MAX / typeSize;
  1.1448 +
  1.1449 +    // This could overflow on 32-bit systems so check max first.
  1.1450 +    size_t byteSize = count * typeSize;
  1.1451 +    if (count > max || !(*output = nsMemory::Alloc(byteSize))) {
  1.1452 +        if (pErr)
  1.1453 +            *pErr = NS_ERROR_OUT_OF_MEMORY;
  1.1454 +
  1.1455 +        return false;
  1.1456 +    }
  1.1457 +
  1.1458 +    memcpy(*output, JS_GetArrayBufferViewData(tArr), byteSize);
  1.1459 +    return true;
  1.1460 +}
  1.1461 +
  1.1462 +// Fast conversion of typed arrays to native using memcpy.
  1.1463 +// No float or double canonicalization is done. Called by
  1.1464 +// JSarray2Native whenever a TypedArray is met. ArrayBuffers
  1.1465 +// are not accepted; create a properly typed array view on them
  1.1466 +// first. The element type of array must match the XPCOM
  1.1467 +// type in size, type and signedness exactly. As an exception,
  1.1468 +// Uint8ClampedArray is allowed for arrays of uint8_t. DataViews
  1.1469 +// are not supported.
  1.1470 +
  1.1471 +// static
  1.1472 +bool
  1.1473 +XPCConvert::JSTypedArray2Native(void** d,
  1.1474 +                                JSObject* jsArray,
  1.1475 +                                uint32_t count,
  1.1476 +                                const nsXPTType& type,
  1.1477 +                                nsresult* pErr)
  1.1478 +{
  1.1479 +    MOZ_ASSERT(jsArray, "bad param");
  1.1480 +    MOZ_ASSERT(d, "bad param");
  1.1481 +    MOZ_ASSERT(JS_IsTypedArrayObject(jsArray), "not a typed array");
  1.1482 +
  1.1483 +    // Check the actual length of the input array against the
  1.1484 +    // given size_is.
  1.1485 +    uint32_t len = JS_GetTypedArrayLength(jsArray);
  1.1486 +    if (len < count) {
  1.1487 +        if (pErr)
  1.1488 +            *pErr = NS_ERROR_XPC_NOT_ENOUGH_ELEMENTS_IN_ARRAY;
  1.1489 +
  1.1490 +        return false;
  1.1491 +    }
  1.1492 +
  1.1493 +    void* output = nullptr;
  1.1494 +
  1.1495 +    switch (JS_GetArrayBufferViewType(jsArray)) {
  1.1496 +    case js::ArrayBufferView::TYPE_INT8:
  1.1497 +        if (!CheckTargetAndPopulate(nsXPTType::T_I8, type,
  1.1498 +                                    sizeof(int8_t), count,
  1.1499 +                                    jsArray, &output, pErr)) {
  1.1500 +            return false;
  1.1501 +        }
  1.1502 +        break;
  1.1503 +
  1.1504 +    case js::ArrayBufferView::TYPE_UINT8:
  1.1505 +    case js::ArrayBufferView::TYPE_UINT8_CLAMPED:
  1.1506 +        if (!CheckTargetAndPopulate(nsXPTType::T_U8, type,
  1.1507 +                                    sizeof(uint8_t), count,
  1.1508 +                                    jsArray, &output, pErr)) {
  1.1509 +            return false;
  1.1510 +        }
  1.1511 +        break;
  1.1512 +
  1.1513 +    case js::ArrayBufferView::TYPE_INT16:
  1.1514 +        if (!CheckTargetAndPopulate(nsXPTType::T_I16, type,
  1.1515 +                                    sizeof(int16_t), count,
  1.1516 +                                    jsArray, &output, pErr)) {
  1.1517 +            return false;
  1.1518 +        }
  1.1519 +        break;
  1.1520 +
  1.1521 +    case js::ArrayBufferView::TYPE_UINT16:
  1.1522 +        if (!CheckTargetAndPopulate(nsXPTType::T_U16, type,
  1.1523 +                                    sizeof(uint16_t), count,
  1.1524 +                                    jsArray, &output, pErr)) {
  1.1525 +            return false;
  1.1526 +        }
  1.1527 +        break;
  1.1528 +
  1.1529 +    case js::ArrayBufferView::TYPE_INT32:
  1.1530 +        if (!CheckTargetAndPopulate(nsXPTType::T_I32, type,
  1.1531 +                                    sizeof(int32_t), count,
  1.1532 +                                    jsArray, &output, pErr)) {
  1.1533 +            return false;
  1.1534 +        }
  1.1535 +        break;
  1.1536 +
  1.1537 +    case js::ArrayBufferView::TYPE_UINT32:
  1.1538 +        if (!CheckTargetAndPopulate(nsXPTType::T_U32, type,
  1.1539 +                                    sizeof(uint32_t), count,
  1.1540 +                                    jsArray, &output, pErr)) {
  1.1541 +            return false;
  1.1542 +        }
  1.1543 +        break;
  1.1544 +
  1.1545 +    case js::ArrayBufferView::TYPE_FLOAT32:
  1.1546 +        if (!CheckTargetAndPopulate(nsXPTType::T_FLOAT, type,
  1.1547 +                                    sizeof(float), count,
  1.1548 +                                    jsArray, &output, pErr)) {
  1.1549 +            return false;
  1.1550 +        }
  1.1551 +        break;
  1.1552 +
  1.1553 +    case js::ArrayBufferView::TYPE_FLOAT64:
  1.1554 +        if (!CheckTargetAndPopulate(nsXPTType::T_DOUBLE, type,
  1.1555 +                                    sizeof(double), count,
  1.1556 +                                    jsArray, &output, pErr)) {
  1.1557 +            return false;
  1.1558 +        }
  1.1559 +        break;
  1.1560 +
  1.1561 +    // Yet another array type was defined? It is not supported yet...
  1.1562 +    default:
  1.1563 +        if (pErr)
  1.1564 +            *pErr = NS_ERROR_XPC_BAD_CONVERT_JS;
  1.1565 +
  1.1566 +        return false;
  1.1567 +    }
  1.1568 +
  1.1569 +    *d = output;
  1.1570 +    if (pErr)
  1.1571 +        *pErr = NS_OK;
  1.1572 +
  1.1573 +    return true;
  1.1574 +}
  1.1575 +
  1.1576 +// static
  1.1577 +bool
  1.1578 +XPCConvert::JSArray2Native(void** d, HandleValue s,
  1.1579 +                           uint32_t count, const nsXPTType& type,
  1.1580 +                           const nsID* iid, nsresult* pErr)
  1.1581 +{
  1.1582 +    MOZ_ASSERT(d, "bad param");
  1.1583 +
  1.1584 +    AutoJSContext cx;
  1.1585 +
  1.1586 +    // XXX add support for getting chars from strings
  1.1587 +
  1.1588 +    // XXX add support to indicate *which* array element was not convertable
  1.1589 +
  1.1590 +    if (s.isNullOrUndefined()) {
  1.1591 +        if (0 != count) {
  1.1592 +            if (pErr)
  1.1593 +                *pErr = NS_ERROR_XPC_NOT_ENOUGH_ELEMENTS_IN_ARRAY;
  1.1594 +            return false;
  1.1595 +        }
  1.1596 +
  1.1597 +        *d = nullptr;
  1.1598 +        return true;
  1.1599 +    }
  1.1600 +
  1.1601 +    if (!s.isObject()) {
  1.1602 +        if (pErr)
  1.1603 +            *pErr = NS_ERROR_XPC_CANT_CONVERT_PRIMITIVE_TO_ARRAY;
  1.1604 +        return false;
  1.1605 +    }
  1.1606 +
  1.1607 +    RootedObject jsarray(cx, &s.toObject());
  1.1608 +
  1.1609 +    // If this is a typed array, then try a fast conversion with memcpy.
  1.1610 +    if (JS_IsTypedArrayObject(jsarray)) {
  1.1611 +        return JSTypedArray2Native(d, jsarray, count, type, pErr);
  1.1612 +    }
  1.1613 +
  1.1614 +    if (!JS_IsArrayObject(cx, jsarray)) {
  1.1615 +        if (pErr)
  1.1616 +            *pErr = NS_ERROR_XPC_CANT_CONVERT_OBJECT_TO_ARRAY;
  1.1617 +        return false;
  1.1618 +    }
  1.1619 +
  1.1620 +    uint32_t len;
  1.1621 +    if (!JS_GetArrayLength(cx, jsarray, &len) || len < count) {
  1.1622 +        if (pErr)
  1.1623 +            *pErr = NS_ERROR_XPC_NOT_ENOUGH_ELEMENTS_IN_ARRAY;
  1.1624 +        return false;
  1.1625 +    }
  1.1626 +
  1.1627 +    if (pErr)
  1.1628 +        *pErr = NS_ERROR_XPC_BAD_CONVERT_JS;
  1.1629 +
  1.1630 +#define POPULATE(_mode, _t)                                                    \
  1.1631 +    PR_BEGIN_MACRO                                                             \
  1.1632 +        cleanupMode = _mode;                                                   \
  1.1633 +        size_t max = UINT32_MAX / sizeof(_t);                                  \
  1.1634 +        if (count > max ||                                                     \
  1.1635 +            nullptr == (array = nsMemory::Alloc(count * sizeof(_t)))) {        \
  1.1636 +            if (pErr)                                                          \
  1.1637 +                *pErr = NS_ERROR_OUT_OF_MEMORY;                                \
  1.1638 +            goto failure;                                                      \
  1.1639 +        }                                                                      \
  1.1640 +        for (initedCount = 0; initedCount < count; initedCount++) {            \
  1.1641 +            if (!JS_GetElement(cx, jsarray, initedCount, &current) ||          \
  1.1642 +                !JSData2Native(((_t*)array)+initedCount, current, type,        \
  1.1643 +                               true, iid, pErr))                               \
  1.1644 +                goto failure;                                                  \
  1.1645 +        }                                                                      \
  1.1646 +    PR_END_MACRO
  1.1647 +
  1.1648 +    // No Action, FRee memory, RElease object
  1.1649 +    enum CleanupMode {na, fr, re};
  1.1650 +
  1.1651 +    CleanupMode cleanupMode;
  1.1652 +
  1.1653 +    void *array = nullptr;
  1.1654 +    uint32_t initedCount;
  1.1655 +    RootedValue current(cx);
  1.1656 +
  1.1657 +    // XXX check IsPtr - esp. to handle array of nsID (as opposed to nsID*)
  1.1658 +    // XXX make extra space at end of char* and wchar* and null termintate
  1.1659 +
  1.1660 +    switch (type.TagPart()) {
  1.1661 +    case nsXPTType::T_I8            : POPULATE(na, int8_t);         break;
  1.1662 +    case nsXPTType::T_I16           : POPULATE(na, int16_t);        break;
  1.1663 +    case nsXPTType::T_I32           : POPULATE(na, int32_t);        break;
  1.1664 +    case nsXPTType::T_I64           : POPULATE(na, int64_t);        break;
  1.1665 +    case nsXPTType::T_U8            : POPULATE(na, uint8_t);        break;
  1.1666 +    case nsXPTType::T_U16           : POPULATE(na, uint16_t);       break;
  1.1667 +    case nsXPTType::T_U32           : POPULATE(na, uint32_t);       break;
  1.1668 +    case nsXPTType::T_U64           : POPULATE(na, uint64_t);       break;
  1.1669 +    case nsXPTType::T_FLOAT         : POPULATE(na, float);          break;
  1.1670 +    case nsXPTType::T_DOUBLE        : POPULATE(na, double);         break;
  1.1671 +    case nsXPTType::T_BOOL          : POPULATE(na, bool);           break;
  1.1672 +    case nsXPTType::T_CHAR          : POPULATE(na, char);           break;
  1.1673 +    case nsXPTType::T_WCHAR         : POPULATE(na, jschar);         break;
  1.1674 +    case nsXPTType::T_VOID          : NS_ERROR("bad type"); goto failure;
  1.1675 +    case nsXPTType::T_IID           : POPULATE(fr, nsID*);          break;
  1.1676 +    case nsXPTType::T_DOMSTRING     : NS_ERROR("bad type"); goto failure;
  1.1677 +    case nsXPTType::T_CHAR_STR      : POPULATE(fr, char*);          break;
  1.1678 +    case nsXPTType::T_WCHAR_STR     : POPULATE(fr, jschar*);        break;
  1.1679 +    case nsXPTType::T_INTERFACE     : POPULATE(re, nsISupports*);   break;
  1.1680 +    case nsXPTType::T_INTERFACE_IS  : POPULATE(re, nsISupports*);   break;
  1.1681 +    case nsXPTType::T_UTF8STRING    : NS_ERROR("bad type"); goto failure;
  1.1682 +    case nsXPTType::T_CSTRING       : NS_ERROR("bad type"); goto failure;
  1.1683 +    case nsXPTType::T_ASTRING       : NS_ERROR("bad type"); goto failure;
  1.1684 +    default                         : NS_ERROR("bad type"); goto failure;
  1.1685 +    }
  1.1686 +
  1.1687 +    *d = array;
  1.1688 +    if (pErr)
  1.1689 +        *pErr = NS_OK;
  1.1690 +    return true;
  1.1691 +
  1.1692 +failure:
  1.1693 +    // we may need to cleanup the partially filled array of converted stuff
  1.1694 +    if (array) {
  1.1695 +        if (cleanupMode == re) {
  1.1696 +            nsISupports** a = (nsISupports**) array;
  1.1697 +            for (uint32_t i = 0; i < initedCount; i++) {
  1.1698 +                nsISupports* p = a[i];
  1.1699 +                NS_IF_RELEASE(p);
  1.1700 +            }
  1.1701 +        } else if (cleanupMode == fr) {
  1.1702 +            void** a = (void**) array;
  1.1703 +            for (uint32_t i = 0; i < initedCount; i++) {
  1.1704 +                void* p = a[i];
  1.1705 +                if (p) nsMemory::Free(p);
  1.1706 +            }
  1.1707 +        }
  1.1708 +        nsMemory::Free(array);
  1.1709 +    }
  1.1710 +
  1.1711 +    return false;
  1.1712 +
  1.1713 +#undef POPULATE
  1.1714 +}
  1.1715 +
  1.1716 +// static
  1.1717 +bool
  1.1718 +XPCConvert::NativeStringWithSize2JS(MutableHandleValue d, const void* s,
  1.1719 +                                    const nsXPTType& type,
  1.1720 +                                    uint32_t count,
  1.1721 +                                    nsresult* pErr)
  1.1722 +{
  1.1723 +    NS_PRECONDITION(s, "bad param");
  1.1724 +
  1.1725 +    AutoJSContext cx;
  1.1726 +    if (pErr)
  1.1727 +        *pErr = NS_ERROR_XPC_BAD_CONVERT_NATIVE;
  1.1728 +
  1.1729 +    switch (type.TagPart()) {
  1.1730 +        case nsXPTType::T_PSTRING_SIZE_IS:
  1.1731 +        {
  1.1732 +            char* p = *((char**)s);
  1.1733 +            if (!p)
  1.1734 +                break;
  1.1735 +            JSString* str;
  1.1736 +            if (!(str = JS_NewStringCopyN(cx, p, count)))
  1.1737 +                return false;
  1.1738 +            d.setString(str);
  1.1739 +            break;
  1.1740 +        }
  1.1741 +        case nsXPTType::T_PWSTRING_SIZE_IS:
  1.1742 +        {
  1.1743 +            jschar* p = *((jschar**)s);
  1.1744 +            if (!p)
  1.1745 +                break;
  1.1746 +            JSString* str;
  1.1747 +            if (!(str = JS_NewUCStringCopyN(cx, p, count)))
  1.1748 +                return false;
  1.1749 +            d.setString(str);
  1.1750 +            break;
  1.1751 +        }
  1.1752 +        default:
  1.1753 +            XPC_LOG_ERROR(("XPCConvert::NativeStringWithSize2JS : unsupported type"));
  1.1754 +            return false;
  1.1755 +    }
  1.1756 +    return true;
  1.1757 +}
  1.1758 +
  1.1759 +// static
  1.1760 +bool
  1.1761 +XPCConvert::JSStringWithSize2Native(void* d, HandleValue s,
  1.1762 +                                    uint32_t count, const nsXPTType& type,
  1.1763 +                                    nsresult* pErr)
  1.1764 +{
  1.1765 +    NS_PRECONDITION(!JSVAL_IS_NULL(s), "bad param");
  1.1766 +    NS_PRECONDITION(d, "bad param");
  1.1767 +
  1.1768 +    AutoJSContext cx;
  1.1769 +    uint32_t len;
  1.1770 +
  1.1771 +    if (pErr)
  1.1772 +        *pErr = NS_ERROR_XPC_BAD_CONVERT_NATIVE;
  1.1773 +
  1.1774 +    switch (type.TagPart()) {
  1.1775 +        case nsXPTType::T_PSTRING_SIZE_IS:
  1.1776 +        {
  1.1777 +            if (JSVAL_IS_VOID(s) || JSVAL_IS_NULL(s)) {
  1.1778 +                if (0 != count) {
  1.1779 +                    if (pErr)
  1.1780 +                        *pErr = NS_ERROR_XPC_NOT_ENOUGH_CHARS_IN_STRING;
  1.1781 +                    return false;
  1.1782 +                }
  1.1783 +                if (0 != count) {
  1.1784 +                    len = (count + 1) * sizeof(char);
  1.1785 +                    if (!(*((void**)d) = nsMemory::Alloc(len)))
  1.1786 +                        return false;
  1.1787 +                    return true;
  1.1788 +                }
  1.1789 +                // else ...
  1.1790 +
  1.1791 +                *((char**)d) = nullptr;
  1.1792 +                return true;
  1.1793 +            }
  1.1794 +
  1.1795 +            JSString* str = ToString(cx, s);
  1.1796 +            if (!str) {
  1.1797 +                return false;
  1.1798 +            }
  1.1799 +
  1.1800 +            size_t length = JS_GetStringEncodingLength(cx, str);
  1.1801 +            if (length == size_t(-1)) {
  1.1802 +                return false;
  1.1803 +            }
  1.1804 +            if (length > count) {
  1.1805 +                if (pErr)
  1.1806 +                    *pErr = NS_ERROR_XPC_NOT_ENOUGH_CHARS_IN_STRING;
  1.1807 +                return false;
  1.1808 +            }
  1.1809 +            len = uint32_t(length);
  1.1810 +
  1.1811 +            if (len < count)
  1.1812 +                len = count;
  1.1813 +
  1.1814 +            uint32_t alloc_len = (len + 1) * sizeof(char);
  1.1815 +            char *buffer = static_cast<char *>(nsMemory::Alloc(alloc_len));
  1.1816 +            if (!buffer) {
  1.1817 +                return false;
  1.1818 +            }
  1.1819 +            JS_EncodeStringToBuffer(cx, str, buffer, len);
  1.1820 +            buffer[len] = '\0';
  1.1821 +            *((char**)d) = buffer;
  1.1822 +
  1.1823 +            return true;
  1.1824 +        }
  1.1825 +
  1.1826 +        case nsXPTType::T_PWSTRING_SIZE_IS:
  1.1827 +        {
  1.1828 +            const jschar* chars=nullptr;
  1.1829 +            JSString* str;
  1.1830 +
  1.1831 +            if (JSVAL_IS_VOID(s) || JSVAL_IS_NULL(s)) {
  1.1832 +                if (0 != count) {
  1.1833 +                    if (pErr)
  1.1834 +                        *pErr = NS_ERROR_XPC_NOT_ENOUGH_CHARS_IN_STRING;
  1.1835 +                    return false;
  1.1836 +                }
  1.1837 +
  1.1838 +                if (0 != count) {
  1.1839 +                    len = (count + 1) * sizeof(jschar);
  1.1840 +                    if (!(*((void**)d) = nsMemory::Alloc(len)))
  1.1841 +                        return false;
  1.1842 +                    return true;
  1.1843 +                }
  1.1844 +
  1.1845 +                // else ...
  1.1846 +                *((const jschar**)d) = nullptr;
  1.1847 +                return true;
  1.1848 +            }
  1.1849 +
  1.1850 +            if (!(str = ToString(cx, s))) {
  1.1851 +                return false;
  1.1852 +            }
  1.1853 +
  1.1854 +            len = JS_GetStringLength(str);
  1.1855 +            if (len > count) {
  1.1856 +                if (pErr)
  1.1857 +                    *pErr = NS_ERROR_XPC_NOT_ENOUGH_CHARS_IN_STRING;
  1.1858 +                return false;
  1.1859 +            }
  1.1860 +            if (len < count)
  1.1861 +                len = count;
  1.1862 +
  1.1863 +            if (!(chars = JS_GetStringCharsZ(cx, str))) {
  1.1864 +                return false;
  1.1865 +            }
  1.1866 +            uint32_t alloc_len = (len + 1) * sizeof(jschar);
  1.1867 +            if (!(*((void**)d) = nsMemory::Alloc(alloc_len))) {
  1.1868 +                // XXX should report error
  1.1869 +                return false;
  1.1870 +            }
  1.1871 +            memcpy(*((jschar**)d), chars, alloc_len);
  1.1872 +            (*((jschar**)d))[count] = 0;
  1.1873 +
  1.1874 +            return true;
  1.1875 +        }
  1.1876 +        default:
  1.1877 +            XPC_LOG_ERROR(("XPCConvert::JSStringWithSize2Native : unsupported type"));
  1.1878 +            return false;
  1.1879 +    }
  1.1880 +}
  1.1881 +

mercurial