js/xpconnect/src/XPCConvert.cpp

Sat, 03 Jan 2015 20:18:00 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Sat, 03 Jan 2015 20:18:00 +0100
branch
TOR_BUG_3246
changeset 7
129ffea94266
permissions
-rw-r--r--

Conditionally enable double key logic according to:
private browsing mode or privacy.thirdparty.isolate preference and
implement in GetCookieStringCommon and FindCookie where it counts...
With some reservations of how to convince FindCookie users to test
condition and pass a nullptr when disabling double key logic.

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

mercurial