js/xpconnect/src/XPCQuickStubs.cpp

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

     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 #include "jsapi.h"
     8 #include "jsfriendapi.h"
     9 #include "jsprf.h"
    10 #include "nsCOMPtr.h"
    11 #include "AccessCheck.h"
    12 #include "WrapperFactory.h"
    13 #include "xpcprivate.h"
    14 #include "XPCInlines.h"
    15 #include "XPCQuickStubs.h"
    16 #include "mozilla/dom/BindingUtils.h"
    17 #include "mozilla/dom/Exceptions.h"
    19 using namespace mozilla;
    20 using namespace JS;
    22 extern const char* xpc_qsStringTable;
    24 static const xpc_qsHashEntry *
    25 LookupEntry(uint32_t tableSize, const xpc_qsHashEntry *table, const nsID &iid)
    26 {
    27     size_t i;
    28     const xpc_qsHashEntry *p;
    30     i = iid.m0 % tableSize;
    31     do
    32     {
    33         p = table + i;
    34         if (p->iid.Equals(iid))
    35             return p;
    36         i = p->chain;
    37     } while (i != XPC_QS_NULL_INDEX);
    38     return nullptr;
    39 }
    41 static const xpc_qsHashEntry *
    42 LookupInterfaceOrAncestor(uint32_t tableSize, const xpc_qsHashEntry *table,
    43                           const nsID &iid)
    44 {
    45     const xpc_qsHashEntry *entry = LookupEntry(tableSize, table, iid);
    46     if (!entry) {
    47         /*
    48          * On a miss, we have to search for every interface the object
    49          * supports, including ancestors.
    50          */
    51         nsCOMPtr<nsIInterfaceInfo> info;
    52         if (NS_FAILED(nsXPConnect::XPConnect()->GetInfoForIID(&iid, getter_AddRefs(info))))
    53             return nullptr;
    55         const nsIID *piid;
    56         for (;;) {
    57             nsCOMPtr<nsIInterfaceInfo> parent;
    58             if (NS_FAILED(info->GetParent(getter_AddRefs(parent))) ||
    59                 !parent ||
    60                 NS_FAILED(parent->GetIIDShared(&piid))) {
    61                 break;
    62             }
    63             entry = LookupEntry(tableSize, table, *piid);
    64             if (entry)
    65                 break;
    66             info.swap(parent);
    67         }
    68     }
    69     return entry;
    70 }
    72 static MOZ_ALWAYS_INLINE bool
    73 HasBitInInterfacesBitmap(JSObject *obj, uint32_t interfaceBit)
    74 {
    75     MOZ_ASSERT(IS_WN_REFLECTOR(obj), "Not a wrapper?");
    77     const XPCWrappedNativeJSClass *clasp =
    78       (const XPCWrappedNativeJSClass*)js::GetObjectClass(obj);
    79     return (clasp->interfacesBitmap & (1 << interfaceBit)) != 0;
    80 }
    82 static void
    83 PointerFinalize(JSFreeOp *fop, JSObject *obj)
    84 {
    85     JSPropertyOp *popp = static_cast<JSPropertyOp *>(JS_GetPrivate(obj));
    86     delete popp;
    87 }
    89 const JSClass
    90 PointerHolderClass = {
    91     "Pointer", JSCLASS_HAS_PRIVATE,
    92     JS_PropertyStub, JS_DeletePropertyStub, JS_PropertyStub, JS_StrictPropertyStub,
    93     JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, PointerFinalize
    94 };
    96 bool
    97 xpc_qsDefineQuickStubs(JSContext *cx, JSObject *protoArg, unsigned flags,
    98                        uint32_t ifacec, const nsIID **interfaces,
    99                        uint32_t tableSize, const xpc_qsHashEntry *table,
   100                        const xpc_qsPropertySpec *propspecs,
   101                        const xpc_qsFunctionSpec *funcspecs,
   102                        const char *stringTable)
   103 {
   104     /*
   105      * Walk interfaces in reverse order to behave like XPConnect when a
   106      * feature is defined in more than one of the interfaces.
   107      *
   108      * XPCNativeSet::FindMethod returns the first matching feature it finds,
   109      * searching the interfaces forward.  Here, definitions toward the
   110      * front of 'interfaces' overwrite those toward the back.
   111      */
   112     RootedObject proto(cx, protoArg);
   113     for (uint32_t i = ifacec; i-- != 0;) {
   114         const nsID &iid = *interfaces[i];
   115         const xpc_qsHashEntry *entry =
   116             LookupInterfaceOrAncestor(tableSize, table, iid);
   118         if (entry) {
   119             for (;;) {
   120                 // Define quick stubs for attributes.
   121                 const xpc_qsPropertySpec *ps = propspecs + entry->prop_index;
   122                 const xpc_qsPropertySpec *ps_end = ps + entry->n_props;
   123                 for ( ; ps < ps_end; ++ps) {
   124                     if (!JS_DefineProperty(cx, proto,
   125                                            stringTable + ps->name_index,
   126                                            JS::UndefinedHandleValue,
   127                                            flags | JSPROP_SHARED | JSPROP_NATIVE_ACCESSORS,
   128                                            (JSPropertyOp)ps->getter,
   129                                            (JSStrictPropertyOp)ps->setter))
   130                         return false;
   131                 }
   133                 // Define quick stubs for methods.
   134                 const xpc_qsFunctionSpec *fs = funcspecs + entry->func_index;
   135                 const xpc_qsFunctionSpec *fs_end = fs + entry->n_funcs;
   136                 for ( ; fs < fs_end; ++fs) {
   137                     if (!JS_DefineFunction(cx, proto,
   138                                            stringTable + fs->name_index,
   139                                            reinterpret_cast<JSNative>(fs->native),
   140                                            fs->arity, flags))
   141                         return false;
   142                 }
   144                 if (entry->newBindingProperties) {
   145                     if (entry->newBindingProperties->regular) {
   146                         mozilla::dom::DefineWebIDLBindingPropertiesOnXPCObject(cx, proto, entry->newBindingProperties->regular, false);
   147                     }
   148                     if (entry->newBindingProperties->chromeOnly &&
   149                         xpc::AccessCheck::isChrome(js::GetContextCompartment(cx))) {
   150                         mozilla::dom::DefineWebIDLBindingPropertiesOnXPCObject(cx, proto, entry->newBindingProperties->chromeOnly, false);
   151                     }
   152                 }
   153                 // Next.
   154                 size_t j = entry->parentInterface;
   155                 if (j == XPC_QS_NULL_INDEX)
   156                     break;
   157                 entry = table + j;
   158             }
   159         }
   160     }
   162     return true;
   163 }
   165 bool
   166 xpc_qsThrow(JSContext *cx, nsresult rv)
   167 {
   168     XPCThrower::Throw(rv, cx);
   169     return false;
   170 }
   172 /**
   173  * Get the interface name and member name (for error messages).
   174  *
   175  * We could instead have each quick stub pass its name to the error-handling
   176  * functions, as that name is statically known.  But that would be redundant;
   177  * the information is handy at runtime anyway.  Also, this code often produces
   178  * a more specific error message, e.g. "[nsIDOMHTMLDocument.appendChild]"
   179  * rather than "[nsIDOMNode.appendChild]".
   180  */
   181 static void
   182 GetMemberInfo(JSObject *obj, jsid memberId, const char **ifaceName)
   183 {
   184     *ifaceName = "Unknown";
   186     // Don't try to generate a useful name if there are security wrappers,
   187     // because it isn't worth the risk of something going wrong just to generate
   188     // an error message. Instead, only handle the simple case where we have the
   189     // reflector in hand.
   190     if (IS_WN_REFLECTOR(obj)) {
   191         XPCWrappedNative *wrapper = XPCWrappedNative::Get(obj);
   192         XPCWrappedNativeProto *proto = wrapper->GetProto();
   193         if (proto) {
   194             XPCNativeSet *set = proto->GetSet();
   195             if (set) {
   196                 XPCNativeMember *member;
   197                 XPCNativeInterface *iface;
   199                 if (set->FindMember(memberId, &member, &iface))
   200                     *ifaceName = iface->GetNameString();
   201             }
   202         }
   203     }
   204 }
   206 static void
   207 GetMethodInfo(JSContext *cx, jsval *vp, const char **ifaceNamep, jsid *memberIdp)
   208 {
   209     CallReceiver call = CallReceiverFromVp(vp);
   210     RootedObject funobj(cx, &call.callee());
   211     MOZ_ASSERT(JS_ObjectIsFunction(cx, funobj),
   212                "JSNative callee should be Function object");
   213     RootedString str(cx, JS_GetFunctionId(JS_GetObjectFunction(funobj)));
   214     RootedId methodId(cx, str ? INTERNED_STRING_TO_JSID(cx, str) : JSID_VOID);
   215     GetMemberInfo(&call.thisv().toObject(), methodId, ifaceNamep);
   216     *memberIdp = methodId;
   217 }
   219 static bool
   220 ThrowCallFailed(JSContext *cx, nsresult rv,
   221                 const char *ifaceName, HandleId memberId, const char *memberName)
   222 {
   223     /* Only one of memberId or memberName should be given. */
   224     MOZ_ASSERT(JSID_IS_VOID(memberId) != !memberName);
   226     // From XPCThrower::ThrowBadResult.
   227     char* sz;
   228     const char* format;
   229     const char* name;
   231     // If the cx already has a pending exception, just throw that.
   232     //
   233     // We used to check here to make sure the exception matched rv (whatever
   234     // that means). But this meant that we'd be calling into JSAPI below with
   235     // a pending exception, which isn't really kosher. The first exception thrown
   236     // should generally take precedence anyway.
   237     if (JS_IsExceptionPending(cx))
   238         return false;
   240     // else...
   242     if (!nsXPCException::NameAndFormatForNSResult(NS_ERROR_XPC_NATIVE_RETURNED_FAILURE, nullptr, &format) ||
   243         !format) {
   244         format = "";
   245     }
   247     JSAutoByteString memberNameBytes;
   248     if (!memberName) {
   249         memberName = JSID_IS_STRING(memberId)
   250                      ? memberNameBytes.encodeLatin1(cx, JSID_TO_STRING(memberId))
   251                      : "unknown";
   252     }
   253     if (nsXPCException::NameAndFormatForNSResult(rv, &name, nullptr)
   254         && name) {
   255         sz = JS_smprintf("%s 0x%x (%s) [%s.%s]",
   256                          format, rv, name, ifaceName, memberName);
   257     } else {
   258         sz = JS_smprintf("%s 0x%x [%s.%s]",
   259                          format, rv, ifaceName, memberName);
   260     }
   262     dom::Throw(cx, rv, sz);
   264     if (sz)
   265         JS_smprintf_free(sz);
   267     return false;
   268 }
   270 bool
   271 xpc_qsThrowGetterSetterFailed(JSContext *cx, nsresult rv, JSObject *obj,
   272                               jsid memberIdArg)
   273 {
   274     RootedId memberId(cx, memberIdArg);
   275     const char *ifaceName;
   276     GetMemberInfo(obj, memberId, &ifaceName);
   277     return ThrowCallFailed(cx, rv, ifaceName, memberId, nullptr);
   278 }
   280 bool
   281 xpc_qsThrowGetterSetterFailed(JSContext *cx, nsresult rv, JSObject *objArg,
   282                               const char* memberName)
   283 {
   284     RootedObject obj(cx, objArg);
   285     JSString *str = JS_InternString(cx, memberName);
   286     if (!str) {
   287         return false;
   288     }
   289     return xpc_qsThrowGetterSetterFailed(cx, rv, obj,
   290                                          INTERNED_STRING_TO_JSID(cx, str));
   291 }
   293 bool
   294 xpc_qsThrowGetterSetterFailed(JSContext *cx, nsresult rv, JSObject *obj,
   295                               uint16_t memberIndex)
   296 {
   297     return xpc_qsThrowGetterSetterFailed(cx, rv, obj,
   298                                          xpc_qsStringTable + memberIndex);
   299 }
   301 bool
   302 xpc_qsThrowMethodFailed(JSContext *cx, nsresult rv, jsval *vp)
   303 {
   304     const char *ifaceName;
   305     RootedId memberId(cx);
   306     GetMethodInfo(cx, vp, &ifaceName, memberId.address());
   307     return ThrowCallFailed(cx, rv, ifaceName, memberId, nullptr);
   308 }
   310 static void
   311 ThrowBadArg(JSContext *cx, nsresult rv, const char *ifaceName,
   312             jsid memberId, const char *memberName, unsigned paramnum)
   313 {
   314     /* Only one memberId or memberName should be given. */
   315     MOZ_ASSERT(JSID_IS_VOID(memberId) != !memberName);
   317     // From XPCThrower::ThrowBadParam.
   318     char* sz;
   319     const char* format;
   321     if (!nsXPCException::NameAndFormatForNSResult(rv, nullptr, &format))
   322         format = "";
   324     JSAutoByteString memberNameBytes;
   325     if (!memberName) {
   326         memberName = JSID_IS_STRING(memberId)
   327                      ? memberNameBytes.encodeLatin1(cx, JSID_TO_STRING(memberId))
   328                      : "unknown";
   329     }
   330     sz = JS_smprintf("%s arg %u [%s.%s]",
   331                      format, (unsigned int) paramnum, ifaceName, memberName);
   333     dom::Throw(cx, rv, sz);
   335     if (sz)
   336         JS_smprintf_free(sz);
   337 }
   339 void
   340 xpc_qsThrowBadArg(JSContext *cx, nsresult rv, jsval *vp, unsigned paramnum)
   341 {
   342     const char *ifaceName;
   343     RootedId memberId(cx);
   344     GetMethodInfo(cx, vp, &ifaceName, memberId.address());
   345     ThrowBadArg(cx, rv, ifaceName, memberId, nullptr, paramnum);
   346 }
   348 void
   349 xpc_qsThrowBadArgWithCcx(XPCCallContext &ccx, nsresult rv, unsigned paramnum)
   350 {
   351     XPCThrower::ThrowBadParam(rv, paramnum, ccx);
   352 }
   354 void
   355 xpc_qsThrowBadArgWithDetails(JSContext *cx, nsresult rv, unsigned paramnum,
   356                              const char *ifaceName, const char *memberName)
   357 {
   358     ThrowBadArg(cx, rv, ifaceName, JSID_VOID, memberName, paramnum);
   359 }
   361 void
   362 xpc_qsThrowBadSetterValue(JSContext *cx, nsresult rv,
   363                           JSObject *obj, jsid propIdArg)
   364 {
   365     RootedId propId(cx, propIdArg);
   366     const char *ifaceName;
   367     GetMemberInfo(obj, propId, &ifaceName);
   368     ThrowBadArg(cx, rv, ifaceName, propId, nullptr, 0);
   369 }
   371 void
   372 xpc_qsThrowBadSetterValue(JSContext *cx, nsresult rv,
   373                           JSObject *objArg, const char* propName)
   374 {
   375     RootedObject obj(cx, objArg);
   376     JSString *str = JS_InternString(cx, propName);
   377     if (!str) {
   378         return;
   379     }
   380     xpc_qsThrowBadSetterValue(cx, rv, obj, INTERNED_STRING_TO_JSID(cx, str));
   381 }
   383 void
   384 xpc_qsThrowBadSetterValue(JSContext *cx, nsresult rv, JSObject *obj,
   385                           uint16_t name_index)
   386 {
   387     xpc_qsThrowBadSetterValue(cx, rv, obj, xpc_qsStringTable + name_index);
   388 }
   390 bool
   391 xpc_qsGetterOnlyPropertyStub(JSContext *cx, HandleObject obj, HandleId id, bool strict,
   392                              MutableHandleValue vp)
   393 {
   394     return JS_ReportErrorFlagsAndNumber(cx,
   395                                         JSREPORT_WARNING | JSREPORT_STRICT |
   396                                         JSREPORT_STRICT_MODE_ERROR,
   397                                         js_GetErrorMessage, nullptr,
   398                                         JSMSG_GETTER_ONLY);
   399 }
   401 bool
   402 xpc_qsGetterOnlyNativeStub(JSContext *cx, unsigned argc, jsval *vp)
   403 {
   404     return JS_ReportErrorFlagsAndNumber(cx,
   405                                         JSREPORT_WARNING | JSREPORT_STRICT |
   406                                         JSREPORT_STRICT_MODE_ERROR,
   407                                         js_GetErrorMessage, nullptr,
   408                                         JSMSG_GETTER_ONLY);
   409 }
   411 xpc_qsDOMString::xpc_qsDOMString(JSContext *cx, HandleValue v,
   412                                  MutableHandleValue pval, bool notpassed,
   413                                  StringificationBehavior nullBehavior,
   414                                  StringificationBehavior undefinedBehavior)
   415 {
   416     typedef implementation_type::char_traits traits;
   417     // From the T_DOMSTRING case in XPCConvert::JSData2Native.
   418     JSString *s = InitOrStringify<traits>(cx, v,
   419                                           pval, notpassed,
   420                                           nullBehavior,
   421                                           undefinedBehavior);
   422     if (!s)
   423         return;
   425     size_t len;
   426     const jschar *chars = JS_GetStringCharsZAndLength(cx, s, &len);
   427     if (!chars) {
   428         mValid = false;
   429         return;
   430     }
   432     new(mBuf) implementation_type(chars, len);
   433     mValid = true;
   434 }
   436 xpc_qsACString::xpc_qsACString(JSContext *cx, HandleValue v,
   437                                MutableHandleValue pval, bool notpassed,
   438                                StringificationBehavior nullBehavior,
   439                                StringificationBehavior undefinedBehavior)
   440 {
   441     typedef implementation_type::char_traits traits;
   442     // From the T_CSTRING case in XPCConvert::JSData2Native.
   443     JSString *s = InitOrStringify<traits>(cx, v,
   444                                           pval, notpassed,
   445                                           nullBehavior,
   446                                           undefinedBehavior);
   447     if (!s)
   448         return;
   450     size_t len = JS_GetStringEncodingLength(cx, s);
   451     if (len == size_t(-1)) {
   452         mValid = false;
   453         return;
   454     }
   456     JSAutoByteString bytes(cx, s);
   457     if (!bytes) {
   458         mValid = false;
   459         return;
   460     }
   462     new(mBuf) implementation_type(bytes.ptr(), len);
   463     mValid = true;
   464 }
   466 xpc_qsAUTF8String::xpc_qsAUTF8String(JSContext *cx, HandleValue v, MutableHandleValue pval, bool notpassed)
   467 {
   468     typedef nsCharTraits<char16_t> traits;
   469     // From the T_UTF8STRING  case in XPCConvert::JSData2Native.
   470     JSString *s = InitOrStringify<traits>(cx, v, pval, notpassed, eNull, eNull);
   471     if (!s)
   472         return;
   474     size_t len;
   475     const char16_t *chars = JS_GetStringCharsZAndLength(cx, s, &len);
   476     if (!chars) {
   477         mValid = false;
   478         return;
   479     }
   481     new(mBuf) implementation_type(chars, len);
   482     mValid = true;
   483 }
   485 static nsresult
   486 getNative(nsISupports *idobj,
   487           HandleObject obj,
   488           const nsIID &iid,
   489           void **ppThis,
   490           nsISupports **pThisRef,
   491           jsval *vp)
   492 {
   493     nsresult rv = idobj->QueryInterface(iid, ppThis);
   494     *pThisRef = static_cast<nsISupports*>(*ppThis);
   495     if (NS_SUCCEEDED(rv))
   496         *vp = OBJECT_TO_JSVAL(obj);
   497     return rv;
   498 }
   500 static inline nsresult
   501 getNativeFromWrapper(JSContext *cx,
   502                      XPCWrappedNative *wrapper,
   503                      const nsIID &iid,
   504                      void **ppThis,
   505                      nsISupports **pThisRef,
   506                      jsval *vp)
   507 {
   508     RootedObject obj(cx, wrapper->GetFlatJSObject());
   509     return getNative(wrapper->GetIdentityObject(), obj, iid, ppThis, pThisRef,
   510                      vp);
   511 }
   514 nsresult
   515 getWrapper(JSContext *cx,
   516            JSObject *obj,
   517            XPCWrappedNative **wrapper,
   518            JSObject **cur,
   519            XPCWrappedNativeTearOff **tearoff)
   520 {
   521     // We can have at most three layers in need of unwrapping here:
   522     // * A (possible) security wrapper
   523     // * A (possible) Xray waiver
   524     // * A (possible) outer window
   525     //
   526     // If we pass stopAtOuter == false, we can handle all three with one call
   527     // to js::CheckedUnwrap.
   528     if (js::IsWrapper(obj)) {
   529         JSObject* inner = js::CheckedUnwrap(obj, /* stopAtOuter = */ false);
   531         // Hack - For historical reasons, wrapped chrome JS objects have been
   532         // passable as native interfaces. We'd like to fix this, but it
   533         // involves fixing the contacts API and PeerConnection to stop using
   534         // COWs. This needs to happen, but for now just preserve the old
   535         // behavior.
   536         //
   537         // Note that there is an identical hack in
   538         // XPCConvert::JSObject2NativeInterface which should be removed if this
   539         // one is.
   540         if (!inner && MOZ_UNLIKELY(xpc::WrapperFactory::IsCOW(obj)))
   541             inner = js::UncheckedUnwrap(obj);
   543         // The safe unwrap might have failed if we encountered an object that
   544         // we're not allowed to unwrap. If it didn't fail though, we should be
   545         // done with wrappers.
   546         if (!inner)
   547             return NS_ERROR_XPC_SECURITY_MANAGER_VETO;
   548         MOZ_ASSERT(!js::IsWrapper(inner));
   550         obj = inner;
   551     }
   553     // Start with sane values.
   554     *wrapper = nullptr;
   555     *cur = nullptr;
   556     *tearoff = nullptr;
   558     if (dom::IsDOMObject(obj)) {
   559         *cur = obj;
   561         return NS_OK;
   562     }
   564     // Handle tearoffs.
   565     //
   566     // If |obj| is of the tearoff class, that means we're dealing with a JS
   567     // object reflection of a particular interface (ie, |foo.nsIBar|). These
   568     // JS objects are parented to their wrapper, so we snag the tearoff object
   569     // along the way (if desired), and then set |obj| to its parent.
   570     const js::Class* clasp = js::GetObjectClass(obj);
   571     if (clasp == &XPC_WN_Tearoff_JSClass) {
   572         *tearoff = (XPCWrappedNativeTearOff*) js::GetObjectPrivate(obj);
   573         obj = js::GetObjectParent(obj);
   574     }
   576     // If we've got a WN, store things the way callers expect. Otherwise, leave
   577     // things null and return.
   578     if (IS_WN_CLASS(clasp))
   579         *wrapper = XPCWrappedNative::Get(obj);
   581     return NS_OK;
   582 }
   584 nsresult
   585 castNative(JSContext *cx,
   586            XPCWrappedNative *wrapper,
   587            JSObject *curArg,
   588            XPCWrappedNativeTearOff *tearoff,
   589            const nsIID &iid,
   590            void **ppThis,
   591            nsISupports **pThisRef,
   592            MutableHandleValue vp)
   593 {
   594     RootedObject cur(cx, curArg);
   595     if (wrapper) {
   596         nsresult rv = getNativeFromWrapper(cx,wrapper, iid, ppThis, pThisRef,
   597                                            vp.address());
   599         if (rv != NS_ERROR_NO_INTERFACE)
   600             return rv;
   601     } else if (cur) {
   602         nsISupports *native;
   603         if (!(native = mozilla::dom::UnwrapDOMObjectToISupports(cur))) {
   604             *pThisRef = nullptr;
   605             return NS_ERROR_ILLEGAL_VALUE;
   606         }
   608         if (NS_SUCCEEDED(getNative(native, cur, iid, ppThis, pThisRef, vp.address()))) {
   609             return NS_OK;
   610         }
   611     }
   613     *pThisRef = nullptr;
   614     return NS_ERROR_XPC_BAD_OP_ON_WN_PROTO;
   615 }
   617 nsISupports*
   618 castNativeFromWrapper(JSContext *cx,
   619                       JSObject *obj,
   620                       uint32_t interfaceBit,
   621                       uint32_t protoID,
   622                       int32_t protoDepth,
   623                       nsISupports **pRef,
   624                       MutableHandleValue pVal,
   625                       nsresult *rv)
   626 {
   627     XPCWrappedNative *wrapper;
   628     XPCWrappedNativeTearOff *tearoff;
   629     JSObject *cur;
   631     if (IS_WN_REFLECTOR(obj)) {
   632         cur = obj;
   633         wrapper = XPCWrappedNative::Get(obj);
   634         tearoff = nullptr;
   635     } else {
   636         *rv = getWrapper(cx, obj, &wrapper, &cur, &tearoff);
   637         if (NS_FAILED(*rv))
   638             return nullptr;
   639     }
   641     nsISupports *native;
   642     if (wrapper) {
   643         native = wrapper->GetIdentityObject();
   644         cur = wrapper->GetFlatJSObject();
   645         if (!native || !HasBitInInterfacesBitmap(cur, interfaceBit)) {
   646             native = nullptr;
   647         }
   648     } else if (cur && protoDepth >= 0) {
   649         const mozilla::dom::DOMClass* domClass =
   650             mozilla::dom::GetDOMClass(cur);
   651         native = mozilla::dom::UnwrapDOMObject<nsISupports>(cur);
   652         if (native &&
   653             (uint32_t)domClass->mInterfaceChain[protoDepth] != protoID) {
   654             native = nullptr;
   655         }
   656     } else {
   657         native = nullptr;
   658     }
   660     if (native) {
   661         *pRef = nullptr;
   662         pVal.setObjectOrNull(cur);
   663         *rv = NS_OK;
   664     } else {
   665         *rv = NS_ERROR_XPC_BAD_CONVERT_JS;
   666     }
   668     return native;
   669 }
   671 bool
   672 xpc_qsUnwrapThisFromCcxImpl(XPCCallContext &ccx,
   673                             const nsIID &iid,
   674                             void **ppThis,
   675                             nsISupports **pThisRef,
   676                             jsval *vp)
   677 {
   678     nsISupports *native = ccx.GetIdentityObject();
   679     if (!native)
   680         return xpc_qsThrow(ccx.GetJSContext(), NS_ERROR_XPC_HAS_BEEN_SHUTDOWN);
   682     RootedObject obj(ccx, ccx.GetFlattenedJSObject());
   683     nsresult rv = getNative(native, obj, iid, ppThis, pThisRef, vp);
   684     if (NS_FAILED(rv))
   685         return xpc_qsThrow(ccx.GetJSContext(), rv);
   686     return true;
   687 }
   689 nsresult
   690 xpc_qsUnwrapArgImpl(JSContext *cx,
   691                     HandleValue v,
   692                     const nsIID &iid,
   693                     void **ppArg,
   694                     nsISupports **ppArgRef,
   695                     MutableHandleValue vp)
   696 {
   697     nsresult rv;
   698     RootedObject src(cx, xpc_qsUnwrapObj(v, ppArgRef, &rv));
   699     if (!src) {
   700         *ppArg = nullptr;
   702         return rv;
   703     }
   705     XPCWrappedNative *wrapper;
   706     XPCWrappedNativeTearOff *tearoff;
   707     JSObject *obj2;
   708     rv = getWrapper(cx, src, &wrapper, &obj2, &tearoff);
   709     NS_ENSURE_SUCCESS(rv, rv);
   711     if (wrapper || obj2) {
   712         if (NS_FAILED(castNative(cx, wrapper, obj2, tearoff, iid, ppArg,
   713                                  ppArgRef, vp)))
   714             return NS_ERROR_XPC_BAD_CONVERT_JS;
   715         return NS_OK;
   716     }
   717     // else...
   718     // Slow path.
   720     // Try to unwrap a slim wrapper.
   721     nsISupports *iface;
   722     if (XPCConvert::GetISupportsFromJSObject(src, &iface)) {
   723         if (!iface || NS_FAILED(iface->QueryInterface(iid, ppArg))) {
   724             *ppArgRef = nullptr;
   725             return NS_ERROR_XPC_BAD_CONVERT_JS;
   726         }
   728         *ppArgRef = static_cast<nsISupports*>(*ppArg);
   729         return NS_OK;
   730     }
   732     // Create the ccx needed for quick stubs.
   733     XPCCallContext ccx(JS_CALLER, cx);
   734     if (!ccx.IsValid()) {
   735         *ppArgRef = nullptr;
   736         return NS_ERROR_XPC_BAD_CONVERT_JS;
   737     }
   739     nsRefPtr<nsXPCWrappedJS> wrappedJS;
   740     rv = nsXPCWrappedJS::GetNewOrUsed(src, iid, getter_AddRefs(wrappedJS));
   741     if (NS_FAILED(rv) || !wrappedJS) {
   742         *ppArgRef = nullptr;
   743         return rv;
   744     }
   746     // We need to go through the QueryInterface logic to make this return
   747     // the right thing for the various 'special' interfaces; e.g.
   748     // nsIPropertyBag. We must use AggregatedQueryInterface in cases where
   749     // there is an outer to avoid nasty recursion.
   750     rv = wrappedJS->QueryInterface(iid, ppArg);
   751     if (NS_SUCCEEDED(rv)) {
   752         *ppArgRef = static_cast<nsISupports*>(*ppArg);
   753         vp.setObjectOrNull(wrappedJS->GetJSObject());
   754     }
   755     return rv;
   756 }
   758 bool
   759 xpc_qsJsvalToCharStr(JSContext *cx, HandleValue v, JSAutoByteString *bytes)
   760 {
   761     MOZ_ASSERT(!bytes->ptr());
   763     if (v.isNullOrUndefined())
   764       return true;
   766     JSString *str = ToString(cx, v);
   767     if (!str)
   768       return false;
   769     return !!bytes->encodeLatin1(cx, str);
   770 }
   772 namespace xpc {
   774 bool
   775 NonVoidStringToJsval(JSContext *cx, nsAString &str, MutableHandleValue rval)
   776 {
   777     nsStringBuffer* sharedBuffer;
   778     if (!XPCStringConvert::ReadableToJSVal(cx, str, &sharedBuffer, rval))
   779       return false;
   781     if (sharedBuffer) {
   782         // The string was shared but ReadableToJSVal didn't addref it.
   783         // Move the ownership from str to jsstr.
   784         str.ForgetSharedBuffer();
   785     }
   786     return true;
   787 }
   789 } // namespace xpc
   791 bool
   792 xpc_qsXPCOMObjectToJsval(JSContext *cx, qsObjectHelper &aHelper,
   793                          const nsIID *iid, XPCNativeInterface **iface,
   794                          MutableHandleValue rval)
   795 {
   796     NS_PRECONDITION(iface, "Who did that and why?");
   798     // From the T_INTERFACE case in XPCConvert::NativeData2JS.
   799     // This is one of the slowest things quick stubs do.
   801     nsresult rv;
   802     if (!XPCConvert::NativeInterface2JSObject(rval, nullptr,
   803                                               aHelper, iid, iface,
   804                                               true, &rv)) {
   805         // I can't tell if NativeInterface2JSObject throws JS exceptions
   806         // or not.  This is a sloppy stab at the right semantics; the
   807         // method really ought to be fixed to behave consistently.
   808         if (!JS_IsExceptionPending(cx))
   809             xpc_qsThrow(cx, NS_FAILED(rv) ? rv : NS_ERROR_UNEXPECTED);
   810         return false;
   811     }
   813 #ifdef DEBUG
   814     JSObject* jsobj = rval.toObjectOrNull();
   815     if (jsobj && !js::GetObjectParent(jsobj))
   816         MOZ_ASSERT(js::GetObjectClass(jsobj)->flags & JSCLASS_IS_GLOBAL,
   817                    "Why did we recreate this wrapper?");
   818 #endif
   820     return true;
   821 }
   823 bool
   824 xpc_qsVariantToJsval(JSContext *aCx,
   825                      nsIVariant *p,
   826                      MutableHandleValue rval)
   827 {
   828     // From the T_INTERFACE case in XPCConvert::NativeData2JS.
   829     // Error handling is in XPCWrappedNative::CallMethod.
   830     if (p) {
   831         nsresult rv;
   832         bool ok = XPCVariant::VariantDataToJS(p, &rv, rval);
   833         if (!ok)
   834             xpc_qsThrow(aCx, rv);
   835         return ok;
   836     }
   837     rval.setNull();
   838     return true;
   839 }
   841 #ifdef DEBUG
   842 void
   843 xpc_qsAssertContextOK(JSContext *cx)
   844 {
   845     XPCJSContextStack* stack = XPCJSRuntime::Get()->GetJSContextStack();
   847     JSContext *topJSContext = stack->Peek();
   849     // This is what we're actually trying to assert here.
   850     MOZ_ASSERT(cx == topJSContext, "wrong context on XPCJSContextStack!");
   851 }
   852 #endif

mercurial