js/xpconnect/src/XPCQuickStubs.cpp

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/js/xpconnect/src/XPCQuickStubs.cpp	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,852 @@
     1.4 +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
     1.5 +/* vim: set ts=8 sts=4 et sw=4 tw=99: */
     1.6 +/* This Source Code Form is subject to the terms of the Mozilla Public
     1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this
     1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     1.9 +
    1.10 +#include "jsapi.h"
    1.11 +#include "jsfriendapi.h"
    1.12 +#include "jsprf.h"
    1.13 +#include "nsCOMPtr.h"
    1.14 +#include "AccessCheck.h"
    1.15 +#include "WrapperFactory.h"
    1.16 +#include "xpcprivate.h"
    1.17 +#include "XPCInlines.h"
    1.18 +#include "XPCQuickStubs.h"
    1.19 +#include "mozilla/dom/BindingUtils.h"
    1.20 +#include "mozilla/dom/Exceptions.h"
    1.21 +
    1.22 +using namespace mozilla;
    1.23 +using namespace JS;
    1.24 +
    1.25 +extern const char* xpc_qsStringTable;
    1.26 +
    1.27 +static const xpc_qsHashEntry *
    1.28 +LookupEntry(uint32_t tableSize, const xpc_qsHashEntry *table, const nsID &iid)
    1.29 +{
    1.30 +    size_t i;
    1.31 +    const xpc_qsHashEntry *p;
    1.32 +
    1.33 +    i = iid.m0 % tableSize;
    1.34 +    do
    1.35 +    {
    1.36 +        p = table + i;
    1.37 +        if (p->iid.Equals(iid))
    1.38 +            return p;
    1.39 +        i = p->chain;
    1.40 +    } while (i != XPC_QS_NULL_INDEX);
    1.41 +    return nullptr;
    1.42 +}
    1.43 +
    1.44 +static const xpc_qsHashEntry *
    1.45 +LookupInterfaceOrAncestor(uint32_t tableSize, const xpc_qsHashEntry *table,
    1.46 +                          const nsID &iid)
    1.47 +{
    1.48 +    const xpc_qsHashEntry *entry = LookupEntry(tableSize, table, iid);
    1.49 +    if (!entry) {
    1.50 +        /*
    1.51 +         * On a miss, we have to search for every interface the object
    1.52 +         * supports, including ancestors.
    1.53 +         */
    1.54 +        nsCOMPtr<nsIInterfaceInfo> info;
    1.55 +        if (NS_FAILED(nsXPConnect::XPConnect()->GetInfoForIID(&iid, getter_AddRefs(info))))
    1.56 +            return nullptr;
    1.57 +
    1.58 +        const nsIID *piid;
    1.59 +        for (;;) {
    1.60 +            nsCOMPtr<nsIInterfaceInfo> parent;
    1.61 +            if (NS_FAILED(info->GetParent(getter_AddRefs(parent))) ||
    1.62 +                !parent ||
    1.63 +                NS_FAILED(parent->GetIIDShared(&piid))) {
    1.64 +                break;
    1.65 +            }
    1.66 +            entry = LookupEntry(tableSize, table, *piid);
    1.67 +            if (entry)
    1.68 +                break;
    1.69 +            info.swap(parent);
    1.70 +        }
    1.71 +    }
    1.72 +    return entry;
    1.73 +}
    1.74 +
    1.75 +static MOZ_ALWAYS_INLINE bool
    1.76 +HasBitInInterfacesBitmap(JSObject *obj, uint32_t interfaceBit)
    1.77 +{
    1.78 +    MOZ_ASSERT(IS_WN_REFLECTOR(obj), "Not a wrapper?");
    1.79 +
    1.80 +    const XPCWrappedNativeJSClass *clasp =
    1.81 +      (const XPCWrappedNativeJSClass*)js::GetObjectClass(obj);
    1.82 +    return (clasp->interfacesBitmap & (1 << interfaceBit)) != 0;
    1.83 +}
    1.84 +
    1.85 +static void
    1.86 +PointerFinalize(JSFreeOp *fop, JSObject *obj)
    1.87 +{
    1.88 +    JSPropertyOp *popp = static_cast<JSPropertyOp *>(JS_GetPrivate(obj));
    1.89 +    delete popp;
    1.90 +}
    1.91 +
    1.92 +const JSClass
    1.93 +PointerHolderClass = {
    1.94 +    "Pointer", JSCLASS_HAS_PRIVATE,
    1.95 +    JS_PropertyStub, JS_DeletePropertyStub, JS_PropertyStub, JS_StrictPropertyStub,
    1.96 +    JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, PointerFinalize
    1.97 +};
    1.98 +
    1.99 +bool
   1.100 +xpc_qsDefineQuickStubs(JSContext *cx, JSObject *protoArg, unsigned flags,
   1.101 +                       uint32_t ifacec, const nsIID **interfaces,
   1.102 +                       uint32_t tableSize, const xpc_qsHashEntry *table,
   1.103 +                       const xpc_qsPropertySpec *propspecs,
   1.104 +                       const xpc_qsFunctionSpec *funcspecs,
   1.105 +                       const char *stringTable)
   1.106 +{
   1.107 +    /*
   1.108 +     * Walk interfaces in reverse order to behave like XPConnect when a
   1.109 +     * feature is defined in more than one of the interfaces.
   1.110 +     *
   1.111 +     * XPCNativeSet::FindMethod returns the first matching feature it finds,
   1.112 +     * searching the interfaces forward.  Here, definitions toward the
   1.113 +     * front of 'interfaces' overwrite those toward the back.
   1.114 +     */
   1.115 +    RootedObject proto(cx, protoArg);
   1.116 +    for (uint32_t i = ifacec; i-- != 0;) {
   1.117 +        const nsID &iid = *interfaces[i];
   1.118 +        const xpc_qsHashEntry *entry =
   1.119 +            LookupInterfaceOrAncestor(tableSize, table, iid);
   1.120 +
   1.121 +        if (entry) {
   1.122 +            for (;;) {
   1.123 +                // Define quick stubs for attributes.
   1.124 +                const xpc_qsPropertySpec *ps = propspecs + entry->prop_index;
   1.125 +                const xpc_qsPropertySpec *ps_end = ps + entry->n_props;
   1.126 +                for ( ; ps < ps_end; ++ps) {
   1.127 +                    if (!JS_DefineProperty(cx, proto,
   1.128 +                                           stringTable + ps->name_index,
   1.129 +                                           JS::UndefinedHandleValue,
   1.130 +                                           flags | JSPROP_SHARED | JSPROP_NATIVE_ACCESSORS,
   1.131 +                                           (JSPropertyOp)ps->getter,
   1.132 +                                           (JSStrictPropertyOp)ps->setter))
   1.133 +                        return false;
   1.134 +                }
   1.135 +
   1.136 +                // Define quick stubs for methods.
   1.137 +                const xpc_qsFunctionSpec *fs = funcspecs + entry->func_index;
   1.138 +                const xpc_qsFunctionSpec *fs_end = fs + entry->n_funcs;
   1.139 +                for ( ; fs < fs_end; ++fs) {
   1.140 +                    if (!JS_DefineFunction(cx, proto,
   1.141 +                                           stringTable + fs->name_index,
   1.142 +                                           reinterpret_cast<JSNative>(fs->native),
   1.143 +                                           fs->arity, flags))
   1.144 +                        return false;
   1.145 +                }
   1.146 +
   1.147 +                if (entry->newBindingProperties) {
   1.148 +                    if (entry->newBindingProperties->regular) {
   1.149 +                        mozilla::dom::DefineWebIDLBindingPropertiesOnXPCObject(cx, proto, entry->newBindingProperties->regular, false);
   1.150 +                    }
   1.151 +                    if (entry->newBindingProperties->chromeOnly &&
   1.152 +                        xpc::AccessCheck::isChrome(js::GetContextCompartment(cx))) {
   1.153 +                        mozilla::dom::DefineWebIDLBindingPropertiesOnXPCObject(cx, proto, entry->newBindingProperties->chromeOnly, false);
   1.154 +                    }
   1.155 +                }
   1.156 +                // Next.
   1.157 +                size_t j = entry->parentInterface;
   1.158 +                if (j == XPC_QS_NULL_INDEX)
   1.159 +                    break;
   1.160 +                entry = table + j;
   1.161 +            }
   1.162 +        }
   1.163 +    }
   1.164 +
   1.165 +    return true;
   1.166 +}
   1.167 +
   1.168 +bool
   1.169 +xpc_qsThrow(JSContext *cx, nsresult rv)
   1.170 +{
   1.171 +    XPCThrower::Throw(rv, cx);
   1.172 +    return false;
   1.173 +}
   1.174 +
   1.175 +/**
   1.176 + * Get the interface name and member name (for error messages).
   1.177 + *
   1.178 + * We could instead have each quick stub pass its name to the error-handling
   1.179 + * functions, as that name is statically known.  But that would be redundant;
   1.180 + * the information is handy at runtime anyway.  Also, this code often produces
   1.181 + * a more specific error message, e.g. "[nsIDOMHTMLDocument.appendChild]"
   1.182 + * rather than "[nsIDOMNode.appendChild]".
   1.183 + */
   1.184 +static void
   1.185 +GetMemberInfo(JSObject *obj, jsid memberId, const char **ifaceName)
   1.186 +{
   1.187 +    *ifaceName = "Unknown";
   1.188 +
   1.189 +    // Don't try to generate a useful name if there are security wrappers,
   1.190 +    // because it isn't worth the risk of something going wrong just to generate
   1.191 +    // an error message. Instead, only handle the simple case where we have the
   1.192 +    // reflector in hand.
   1.193 +    if (IS_WN_REFLECTOR(obj)) {
   1.194 +        XPCWrappedNative *wrapper = XPCWrappedNative::Get(obj);
   1.195 +        XPCWrappedNativeProto *proto = wrapper->GetProto();
   1.196 +        if (proto) {
   1.197 +            XPCNativeSet *set = proto->GetSet();
   1.198 +            if (set) {
   1.199 +                XPCNativeMember *member;
   1.200 +                XPCNativeInterface *iface;
   1.201 +
   1.202 +                if (set->FindMember(memberId, &member, &iface))
   1.203 +                    *ifaceName = iface->GetNameString();
   1.204 +            }
   1.205 +        }
   1.206 +    }
   1.207 +}
   1.208 +
   1.209 +static void
   1.210 +GetMethodInfo(JSContext *cx, jsval *vp, const char **ifaceNamep, jsid *memberIdp)
   1.211 +{
   1.212 +    CallReceiver call = CallReceiverFromVp(vp);
   1.213 +    RootedObject funobj(cx, &call.callee());
   1.214 +    MOZ_ASSERT(JS_ObjectIsFunction(cx, funobj),
   1.215 +               "JSNative callee should be Function object");
   1.216 +    RootedString str(cx, JS_GetFunctionId(JS_GetObjectFunction(funobj)));
   1.217 +    RootedId methodId(cx, str ? INTERNED_STRING_TO_JSID(cx, str) : JSID_VOID);
   1.218 +    GetMemberInfo(&call.thisv().toObject(), methodId, ifaceNamep);
   1.219 +    *memberIdp = methodId;
   1.220 +}
   1.221 +
   1.222 +static bool
   1.223 +ThrowCallFailed(JSContext *cx, nsresult rv,
   1.224 +                const char *ifaceName, HandleId memberId, const char *memberName)
   1.225 +{
   1.226 +    /* Only one of memberId or memberName should be given. */
   1.227 +    MOZ_ASSERT(JSID_IS_VOID(memberId) != !memberName);
   1.228 +
   1.229 +    // From XPCThrower::ThrowBadResult.
   1.230 +    char* sz;
   1.231 +    const char* format;
   1.232 +    const char* name;
   1.233 +
   1.234 +    // If the cx already has a pending exception, just throw that.
   1.235 +    //
   1.236 +    // We used to check here to make sure the exception matched rv (whatever
   1.237 +    // that means). But this meant that we'd be calling into JSAPI below with
   1.238 +    // a pending exception, which isn't really kosher. The first exception thrown
   1.239 +    // should generally take precedence anyway.
   1.240 +    if (JS_IsExceptionPending(cx))
   1.241 +        return false;
   1.242 +
   1.243 +    // else...
   1.244 +
   1.245 +    if (!nsXPCException::NameAndFormatForNSResult(NS_ERROR_XPC_NATIVE_RETURNED_FAILURE, nullptr, &format) ||
   1.246 +        !format) {
   1.247 +        format = "";
   1.248 +    }
   1.249 +
   1.250 +    JSAutoByteString memberNameBytes;
   1.251 +    if (!memberName) {
   1.252 +        memberName = JSID_IS_STRING(memberId)
   1.253 +                     ? memberNameBytes.encodeLatin1(cx, JSID_TO_STRING(memberId))
   1.254 +                     : "unknown";
   1.255 +    }
   1.256 +    if (nsXPCException::NameAndFormatForNSResult(rv, &name, nullptr)
   1.257 +        && name) {
   1.258 +        sz = JS_smprintf("%s 0x%x (%s) [%s.%s]",
   1.259 +                         format, rv, name, ifaceName, memberName);
   1.260 +    } else {
   1.261 +        sz = JS_smprintf("%s 0x%x [%s.%s]",
   1.262 +                         format, rv, ifaceName, memberName);
   1.263 +    }
   1.264 +
   1.265 +    dom::Throw(cx, rv, sz);
   1.266 +
   1.267 +    if (sz)
   1.268 +        JS_smprintf_free(sz);
   1.269 +
   1.270 +    return false;
   1.271 +}
   1.272 +
   1.273 +bool
   1.274 +xpc_qsThrowGetterSetterFailed(JSContext *cx, nsresult rv, JSObject *obj,
   1.275 +                              jsid memberIdArg)
   1.276 +{
   1.277 +    RootedId memberId(cx, memberIdArg);
   1.278 +    const char *ifaceName;
   1.279 +    GetMemberInfo(obj, memberId, &ifaceName);
   1.280 +    return ThrowCallFailed(cx, rv, ifaceName, memberId, nullptr);
   1.281 +}
   1.282 +
   1.283 +bool
   1.284 +xpc_qsThrowGetterSetterFailed(JSContext *cx, nsresult rv, JSObject *objArg,
   1.285 +                              const char* memberName)
   1.286 +{
   1.287 +    RootedObject obj(cx, objArg);
   1.288 +    JSString *str = JS_InternString(cx, memberName);
   1.289 +    if (!str) {
   1.290 +        return false;
   1.291 +    }
   1.292 +    return xpc_qsThrowGetterSetterFailed(cx, rv, obj,
   1.293 +                                         INTERNED_STRING_TO_JSID(cx, str));
   1.294 +}
   1.295 +
   1.296 +bool
   1.297 +xpc_qsThrowGetterSetterFailed(JSContext *cx, nsresult rv, JSObject *obj,
   1.298 +                              uint16_t memberIndex)
   1.299 +{
   1.300 +    return xpc_qsThrowGetterSetterFailed(cx, rv, obj,
   1.301 +                                         xpc_qsStringTable + memberIndex);
   1.302 +}
   1.303 +
   1.304 +bool
   1.305 +xpc_qsThrowMethodFailed(JSContext *cx, nsresult rv, jsval *vp)
   1.306 +{
   1.307 +    const char *ifaceName;
   1.308 +    RootedId memberId(cx);
   1.309 +    GetMethodInfo(cx, vp, &ifaceName, memberId.address());
   1.310 +    return ThrowCallFailed(cx, rv, ifaceName, memberId, nullptr);
   1.311 +}
   1.312 +
   1.313 +static void
   1.314 +ThrowBadArg(JSContext *cx, nsresult rv, const char *ifaceName,
   1.315 +            jsid memberId, const char *memberName, unsigned paramnum)
   1.316 +{
   1.317 +    /* Only one memberId or memberName should be given. */
   1.318 +    MOZ_ASSERT(JSID_IS_VOID(memberId) != !memberName);
   1.319 +
   1.320 +    // From XPCThrower::ThrowBadParam.
   1.321 +    char* sz;
   1.322 +    const char* format;
   1.323 +
   1.324 +    if (!nsXPCException::NameAndFormatForNSResult(rv, nullptr, &format))
   1.325 +        format = "";
   1.326 +
   1.327 +    JSAutoByteString memberNameBytes;
   1.328 +    if (!memberName) {
   1.329 +        memberName = JSID_IS_STRING(memberId)
   1.330 +                     ? memberNameBytes.encodeLatin1(cx, JSID_TO_STRING(memberId))
   1.331 +                     : "unknown";
   1.332 +    }
   1.333 +    sz = JS_smprintf("%s arg %u [%s.%s]",
   1.334 +                     format, (unsigned int) paramnum, ifaceName, memberName);
   1.335 +
   1.336 +    dom::Throw(cx, rv, sz);
   1.337 +
   1.338 +    if (sz)
   1.339 +        JS_smprintf_free(sz);
   1.340 +}
   1.341 +
   1.342 +void
   1.343 +xpc_qsThrowBadArg(JSContext *cx, nsresult rv, jsval *vp, unsigned paramnum)
   1.344 +{
   1.345 +    const char *ifaceName;
   1.346 +    RootedId memberId(cx);
   1.347 +    GetMethodInfo(cx, vp, &ifaceName, memberId.address());
   1.348 +    ThrowBadArg(cx, rv, ifaceName, memberId, nullptr, paramnum);
   1.349 +}
   1.350 +
   1.351 +void
   1.352 +xpc_qsThrowBadArgWithCcx(XPCCallContext &ccx, nsresult rv, unsigned paramnum)
   1.353 +{
   1.354 +    XPCThrower::ThrowBadParam(rv, paramnum, ccx);
   1.355 +}
   1.356 +
   1.357 +void
   1.358 +xpc_qsThrowBadArgWithDetails(JSContext *cx, nsresult rv, unsigned paramnum,
   1.359 +                             const char *ifaceName, const char *memberName)
   1.360 +{
   1.361 +    ThrowBadArg(cx, rv, ifaceName, JSID_VOID, memberName, paramnum);
   1.362 +}
   1.363 +
   1.364 +void
   1.365 +xpc_qsThrowBadSetterValue(JSContext *cx, nsresult rv,
   1.366 +                          JSObject *obj, jsid propIdArg)
   1.367 +{
   1.368 +    RootedId propId(cx, propIdArg);
   1.369 +    const char *ifaceName;
   1.370 +    GetMemberInfo(obj, propId, &ifaceName);
   1.371 +    ThrowBadArg(cx, rv, ifaceName, propId, nullptr, 0);
   1.372 +}
   1.373 +
   1.374 +void
   1.375 +xpc_qsThrowBadSetterValue(JSContext *cx, nsresult rv,
   1.376 +                          JSObject *objArg, const char* propName)
   1.377 +{
   1.378 +    RootedObject obj(cx, objArg);
   1.379 +    JSString *str = JS_InternString(cx, propName);
   1.380 +    if (!str) {
   1.381 +        return;
   1.382 +    }
   1.383 +    xpc_qsThrowBadSetterValue(cx, rv, obj, INTERNED_STRING_TO_JSID(cx, str));
   1.384 +}
   1.385 +
   1.386 +void
   1.387 +xpc_qsThrowBadSetterValue(JSContext *cx, nsresult rv, JSObject *obj,
   1.388 +                          uint16_t name_index)
   1.389 +{
   1.390 +    xpc_qsThrowBadSetterValue(cx, rv, obj, xpc_qsStringTable + name_index);
   1.391 +}
   1.392 +
   1.393 +bool
   1.394 +xpc_qsGetterOnlyPropertyStub(JSContext *cx, HandleObject obj, HandleId id, bool strict,
   1.395 +                             MutableHandleValue vp)
   1.396 +{
   1.397 +    return JS_ReportErrorFlagsAndNumber(cx,
   1.398 +                                        JSREPORT_WARNING | JSREPORT_STRICT |
   1.399 +                                        JSREPORT_STRICT_MODE_ERROR,
   1.400 +                                        js_GetErrorMessage, nullptr,
   1.401 +                                        JSMSG_GETTER_ONLY);
   1.402 +}
   1.403 +
   1.404 +bool
   1.405 +xpc_qsGetterOnlyNativeStub(JSContext *cx, unsigned argc, jsval *vp)
   1.406 +{
   1.407 +    return JS_ReportErrorFlagsAndNumber(cx,
   1.408 +                                        JSREPORT_WARNING | JSREPORT_STRICT |
   1.409 +                                        JSREPORT_STRICT_MODE_ERROR,
   1.410 +                                        js_GetErrorMessage, nullptr,
   1.411 +                                        JSMSG_GETTER_ONLY);
   1.412 +}
   1.413 +
   1.414 +xpc_qsDOMString::xpc_qsDOMString(JSContext *cx, HandleValue v,
   1.415 +                                 MutableHandleValue pval, bool notpassed,
   1.416 +                                 StringificationBehavior nullBehavior,
   1.417 +                                 StringificationBehavior undefinedBehavior)
   1.418 +{
   1.419 +    typedef implementation_type::char_traits traits;
   1.420 +    // From the T_DOMSTRING case in XPCConvert::JSData2Native.
   1.421 +    JSString *s = InitOrStringify<traits>(cx, v,
   1.422 +                                          pval, notpassed,
   1.423 +                                          nullBehavior,
   1.424 +                                          undefinedBehavior);
   1.425 +    if (!s)
   1.426 +        return;
   1.427 +
   1.428 +    size_t len;
   1.429 +    const jschar *chars = JS_GetStringCharsZAndLength(cx, s, &len);
   1.430 +    if (!chars) {
   1.431 +        mValid = false;
   1.432 +        return;
   1.433 +    }
   1.434 +
   1.435 +    new(mBuf) implementation_type(chars, len);
   1.436 +    mValid = true;
   1.437 +}
   1.438 +
   1.439 +xpc_qsACString::xpc_qsACString(JSContext *cx, HandleValue v,
   1.440 +                               MutableHandleValue pval, bool notpassed,
   1.441 +                               StringificationBehavior nullBehavior,
   1.442 +                               StringificationBehavior undefinedBehavior)
   1.443 +{
   1.444 +    typedef implementation_type::char_traits traits;
   1.445 +    // From the T_CSTRING case in XPCConvert::JSData2Native.
   1.446 +    JSString *s = InitOrStringify<traits>(cx, v,
   1.447 +                                          pval, notpassed,
   1.448 +                                          nullBehavior,
   1.449 +                                          undefinedBehavior);
   1.450 +    if (!s)
   1.451 +        return;
   1.452 +
   1.453 +    size_t len = JS_GetStringEncodingLength(cx, s);
   1.454 +    if (len == size_t(-1)) {
   1.455 +        mValid = false;
   1.456 +        return;
   1.457 +    }
   1.458 +
   1.459 +    JSAutoByteString bytes(cx, s);
   1.460 +    if (!bytes) {
   1.461 +        mValid = false;
   1.462 +        return;
   1.463 +    }
   1.464 +
   1.465 +    new(mBuf) implementation_type(bytes.ptr(), len);
   1.466 +    mValid = true;
   1.467 +}
   1.468 +
   1.469 +xpc_qsAUTF8String::xpc_qsAUTF8String(JSContext *cx, HandleValue v, MutableHandleValue pval, bool notpassed)
   1.470 +{
   1.471 +    typedef nsCharTraits<char16_t> traits;
   1.472 +    // From the T_UTF8STRING  case in XPCConvert::JSData2Native.
   1.473 +    JSString *s = InitOrStringify<traits>(cx, v, pval, notpassed, eNull, eNull);
   1.474 +    if (!s)
   1.475 +        return;
   1.476 +
   1.477 +    size_t len;
   1.478 +    const char16_t *chars = JS_GetStringCharsZAndLength(cx, s, &len);
   1.479 +    if (!chars) {
   1.480 +        mValid = false;
   1.481 +        return;
   1.482 +    }
   1.483 +
   1.484 +    new(mBuf) implementation_type(chars, len);
   1.485 +    mValid = true;
   1.486 +}
   1.487 +
   1.488 +static nsresult
   1.489 +getNative(nsISupports *idobj,
   1.490 +          HandleObject obj,
   1.491 +          const nsIID &iid,
   1.492 +          void **ppThis,
   1.493 +          nsISupports **pThisRef,
   1.494 +          jsval *vp)
   1.495 +{
   1.496 +    nsresult rv = idobj->QueryInterface(iid, ppThis);
   1.497 +    *pThisRef = static_cast<nsISupports*>(*ppThis);
   1.498 +    if (NS_SUCCEEDED(rv))
   1.499 +        *vp = OBJECT_TO_JSVAL(obj);
   1.500 +    return rv;
   1.501 +}
   1.502 +
   1.503 +static inline nsresult
   1.504 +getNativeFromWrapper(JSContext *cx,
   1.505 +                     XPCWrappedNative *wrapper,
   1.506 +                     const nsIID &iid,
   1.507 +                     void **ppThis,
   1.508 +                     nsISupports **pThisRef,
   1.509 +                     jsval *vp)
   1.510 +{
   1.511 +    RootedObject obj(cx, wrapper->GetFlatJSObject());
   1.512 +    return getNative(wrapper->GetIdentityObject(), obj, iid, ppThis, pThisRef,
   1.513 +                     vp);
   1.514 +}
   1.515 +
   1.516 +
   1.517 +nsresult
   1.518 +getWrapper(JSContext *cx,
   1.519 +           JSObject *obj,
   1.520 +           XPCWrappedNative **wrapper,
   1.521 +           JSObject **cur,
   1.522 +           XPCWrappedNativeTearOff **tearoff)
   1.523 +{
   1.524 +    // We can have at most three layers in need of unwrapping here:
   1.525 +    // * A (possible) security wrapper
   1.526 +    // * A (possible) Xray waiver
   1.527 +    // * A (possible) outer window
   1.528 +    //
   1.529 +    // If we pass stopAtOuter == false, we can handle all three with one call
   1.530 +    // to js::CheckedUnwrap.
   1.531 +    if (js::IsWrapper(obj)) {
   1.532 +        JSObject* inner = js::CheckedUnwrap(obj, /* stopAtOuter = */ false);
   1.533 +
   1.534 +        // Hack - For historical reasons, wrapped chrome JS objects have been
   1.535 +        // passable as native interfaces. We'd like to fix this, but it
   1.536 +        // involves fixing the contacts API and PeerConnection to stop using
   1.537 +        // COWs. This needs to happen, but for now just preserve the old
   1.538 +        // behavior.
   1.539 +        //
   1.540 +        // Note that there is an identical hack in
   1.541 +        // XPCConvert::JSObject2NativeInterface which should be removed if this
   1.542 +        // one is.
   1.543 +        if (!inner && MOZ_UNLIKELY(xpc::WrapperFactory::IsCOW(obj)))
   1.544 +            inner = js::UncheckedUnwrap(obj);
   1.545 +
   1.546 +        // The safe unwrap might have failed if we encountered an object that
   1.547 +        // we're not allowed to unwrap. If it didn't fail though, we should be
   1.548 +        // done with wrappers.
   1.549 +        if (!inner)
   1.550 +            return NS_ERROR_XPC_SECURITY_MANAGER_VETO;
   1.551 +        MOZ_ASSERT(!js::IsWrapper(inner));
   1.552 +
   1.553 +        obj = inner;
   1.554 +    }
   1.555 +
   1.556 +    // Start with sane values.
   1.557 +    *wrapper = nullptr;
   1.558 +    *cur = nullptr;
   1.559 +    *tearoff = nullptr;
   1.560 +
   1.561 +    if (dom::IsDOMObject(obj)) {
   1.562 +        *cur = obj;
   1.563 +
   1.564 +        return NS_OK;
   1.565 +    }
   1.566 +
   1.567 +    // Handle tearoffs.
   1.568 +    //
   1.569 +    // If |obj| is of the tearoff class, that means we're dealing with a JS
   1.570 +    // object reflection of a particular interface (ie, |foo.nsIBar|). These
   1.571 +    // JS objects are parented to their wrapper, so we snag the tearoff object
   1.572 +    // along the way (if desired), and then set |obj| to its parent.
   1.573 +    const js::Class* clasp = js::GetObjectClass(obj);
   1.574 +    if (clasp == &XPC_WN_Tearoff_JSClass) {
   1.575 +        *tearoff = (XPCWrappedNativeTearOff*) js::GetObjectPrivate(obj);
   1.576 +        obj = js::GetObjectParent(obj);
   1.577 +    }
   1.578 +
   1.579 +    // If we've got a WN, store things the way callers expect. Otherwise, leave
   1.580 +    // things null and return.
   1.581 +    if (IS_WN_CLASS(clasp))
   1.582 +        *wrapper = XPCWrappedNative::Get(obj);
   1.583 +
   1.584 +    return NS_OK;
   1.585 +}
   1.586 +
   1.587 +nsresult
   1.588 +castNative(JSContext *cx,
   1.589 +           XPCWrappedNative *wrapper,
   1.590 +           JSObject *curArg,
   1.591 +           XPCWrappedNativeTearOff *tearoff,
   1.592 +           const nsIID &iid,
   1.593 +           void **ppThis,
   1.594 +           nsISupports **pThisRef,
   1.595 +           MutableHandleValue vp)
   1.596 +{
   1.597 +    RootedObject cur(cx, curArg);
   1.598 +    if (wrapper) {
   1.599 +        nsresult rv = getNativeFromWrapper(cx,wrapper, iid, ppThis, pThisRef,
   1.600 +                                           vp.address());
   1.601 +
   1.602 +        if (rv != NS_ERROR_NO_INTERFACE)
   1.603 +            return rv;
   1.604 +    } else if (cur) {
   1.605 +        nsISupports *native;
   1.606 +        if (!(native = mozilla::dom::UnwrapDOMObjectToISupports(cur))) {
   1.607 +            *pThisRef = nullptr;
   1.608 +            return NS_ERROR_ILLEGAL_VALUE;
   1.609 +        }
   1.610 +
   1.611 +        if (NS_SUCCEEDED(getNative(native, cur, iid, ppThis, pThisRef, vp.address()))) {
   1.612 +            return NS_OK;
   1.613 +        }
   1.614 +    }
   1.615 +
   1.616 +    *pThisRef = nullptr;
   1.617 +    return NS_ERROR_XPC_BAD_OP_ON_WN_PROTO;
   1.618 +}
   1.619 +
   1.620 +nsISupports*
   1.621 +castNativeFromWrapper(JSContext *cx,
   1.622 +                      JSObject *obj,
   1.623 +                      uint32_t interfaceBit,
   1.624 +                      uint32_t protoID,
   1.625 +                      int32_t protoDepth,
   1.626 +                      nsISupports **pRef,
   1.627 +                      MutableHandleValue pVal,
   1.628 +                      nsresult *rv)
   1.629 +{
   1.630 +    XPCWrappedNative *wrapper;
   1.631 +    XPCWrappedNativeTearOff *tearoff;
   1.632 +    JSObject *cur;
   1.633 +
   1.634 +    if (IS_WN_REFLECTOR(obj)) {
   1.635 +        cur = obj;
   1.636 +        wrapper = XPCWrappedNative::Get(obj);
   1.637 +        tearoff = nullptr;
   1.638 +    } else {
   1.639 +        *rv = getWrapper(cx, obj, &wrapper, &cur, &tearoff);
   1.640 +        if (NS_FAILED(*rv))
   1.641 +            return nullptr;
   1.642 +    }
   1.643 +
   1.644 +    nsISupports *native;
   1.645 +    if (wrapper) {
   1.646 +        native = wrapper->GetIdentityObject();
   1.647 +        cur = wrapper->GetFlatJSObject();
   1.648 +        if (!native || !HasBitInInterfacesBitmap(cur, interfaceBit)) {
   1.649 +            native = nullptr;
   1.650 +        }
   1.651 +    } else if (cur && protoDepth >= 0) {
   1.652 +        const mozilla::dom::DOMClass* domClass =
   1.653 +            mozilla::dom::GetDOMClass(cur);
   1.654 +        native = mozilla::dom::UnwrapDOMObject<nsISupports>(cur);
   1.655 +        if (native &&
   1.656 +            (uint32_t)domClass->mInterfaceChain[protoDepth] != protoID) {
   1.657 +            native = nullptr;
   1.658 +        }
   1.659 +    } else {
   1.660 +        native = nullptr;
   1.661 +    }
   1.662 +
   1.663 +    if (native) {
   1.664 +        *pRef = nullptr;
   1.665 +        pVal.setObjectOrNull(cur);
   1.666 +        *rv = NS_OK;
   1.667 +    } else {
   1.668 +        *rv = NS_ERROR_XPC_BAD_CONVERT_JS;
   1.669 +    }
   1.670 +
   1.671 +    return native;
   1.672 +}
   1.673 +
   1.674 +bool
   1.675 +xpc_qsUnwrapThisFromCcxImpl(XPCCallContext &ccx,
   1.676 +                            const nsIID &iid,
   1.677 +                            void **ppThis,
   1.678 +                            nsISupports **pThisRef,
   1.679 +                            jsval *vp)
   1.680 +{
   1.681 +    nsISupports *native = ccx.GetIdentityObject();
   1.682 +    if (!native)
   1.683 +        return xpc_qsThrow(ccx.GetJSContext(), NS_ERROR_XPC_HAS_BEEN_SHUTDOWN);
   1.684 +
   1.685 +    RootedObject obj(ccx, ccx.GetFlattenedJSObject());
   1.686 +    nsresult rv = getNative(native, obj, iid, ppThis, pThisRef, vp);
   1.687 +    if (NS_FAILED(rv))
   1.688 +        return xpc_qsThrow(ccx.GetJSContext(), rv);
   1.689 +    return true;
   1.690 +}
   1.691 +
   1.692 +nsresult
   1.693 +xpc_qsUnwrapArgImpl(JSContext *cx,
   1.694 +                    HandleValue v,
   1.695 +                    const nsIID &iid,
   1.696 +                    void **ppArg,
   1.697 +                    nsISupports **ppArgRef,
   1.698 +                    MutableHandleValue vp)
   1.699 +{
   1.700 +    nsresult rv;
   1.701 +    RootedObject src(cx, xpc_qsUnwrapObj(v, ppArgRef, &rv));
   1.702 +    if (!src) {
   1.703 +        *ppArg = nullptr;
   1.704 +
   1.705 +        return rv;
   1.706 +    }
   1.707 +
   1.708 +    XPCWrappedNative *wrapper;
   1.709 +    XPCWrappedNativeTearOff *tearoff;
   1.710 +    JSObject *obj2;
   1.711 +    rv = getWrapper(cx, src, &wrapper, &obj2, &tearoff);
   1.712 +    NS_ENSURE_SUCCESS(rv, rv);
   1.713 +
   1.714 +    if (wrapper || obj2) {
   1.715 +        if (NS_FAILED(castNative(cx, wrapper, obj2, tearoff, iid, ppArg,
   1.716 +                                 ppArgRef, vp)))
   1.717 +            return NS_ERROR_XPC_BAD_CONVERT_JS;
   1.718 +        return NS_OK;
   1.719 +    }
   1.720 +    // else...
   1.721 +    // Slow path.
   1.722 +
   1.723 +    // Try to unwrap a slim wrapper.
   1.724 +    nsISupports *iface;
   1.725 +    if (XPCConvert::GetISupportsFromJSObject(src, &iface)) {
   1.726 +        if (!iface || NS_FAILED(iface->QueryInterface(iid, ppArg))) {
   1.727 +            *ppArgRef = nullptr;
   1.728 +            return NS_ERROR_XPC_BAD_CONVERT_JS;
   1.729 +        }
   1.730 +
   1.731 +        *ppArgRef = static_cast<nsISupports*>(*ppArg);
   1.732 +        return NS_OK;
   1.733 +    }
   1.734 +
   1.735 +    // Create the ccx needed for quick stubs.
   1.736 +    XPCCallContext ccx(JS_CALLER, cx);
   1.737 +    if (!ccx.IsValid()) {
   1.738 +        *ppArgRef = nullptr;
   1.739 +        return NS_ERROR_XPC_BAD_CONVERT_JS;
   1.740 +    }
   1.741 +
   1.742 +    nsRefPtr<nsXPCWrappedJS> wrappedJS;
   1.743 +    rv = nsXPCWrappedJS::GetNewOrUsed(src, iid, getter_AddRefs(wrappedJS));
   1.744 +    if (NS_FAILED(rv) || !wrappedJS) {
   1.745 +        *ppArgRef = nullptr;
   1.746 +        return rv;
   1.747 +    }
   1.748 +
   1.749 +    // We need to go through the QueryInterface logic to make this return
   1.750 +    // the right thing for the various 'special' interfaces; e.g.
   1.751 +    // nsIPropertyBag. We must use AggregatedQueryInterface in cases where
   1.752 +    // there is an outer to avoid nasty recursion.
   1.753 +    rv = wrappedJS->QueryInterface(iid, ppArg);
   1.754 +    if (NS_SUCCEEDED(rv)) {
   1.755 +        *ppArgRef = static_cast<nsISupports*>(*ppArg);
   1.756 +        vp.setObjectOrNull(wrappedJS->GetJSObject());
   1.757 +    }
   1.758 +    return rv;
   1.759 +}
   1.760 +
   1.761 +bool
   1.762 +xpc_qsJsvalToCharStr(JSContext *cx, HandleValue v, JSAutoByteString *bytes)
   1.763 +{
   1.764 +    MOZ_ASSERT(!bytes->ptr());
   1.765 +
   1.766 +    if (v.isNullOrUndefined())
   1.767 +      return true;
   1.768 +
   1.769 +    JSString *str = ToString(cx, v);
   1.770 +    if (!str)
   1.771 +      return false;
   1.772 +    return !!bytes->encodeLatin1(cx, str);
   1.773 +}
   1.774 +
   1.775 +namespace xpc {
   1.776 +
   1.777 +bool
   1.778 +NonVoidStringToJsval(JSContext *cx, nsAString &str, MutableHandleValue rval)
   1.779 +{
   1.780 +    nsStringBuffer* sharedBuffer;
   1.781 +    if (!XPCStringConvert::ReadableToJSVal(cx, str, &sharedBuffer, rval))
   1.782 +      return false;
   1.783 +
   1.784 +    if (sharedBuffer) {
   1.785 +        // The string was shared but ReadableToJSVal didn't addref it.
   1.786 +        // Move the ownership from str to jsstr.
   1.787 +        str.ForgetSharedBuffer();
   1.788 +    }
   1.789 +    return true;
   1.790 +}
   1.791 +
   1.792 +} // namespace xpc
   1.793 +
   1.794 +bool
   1.795 +xpc_qsXPCOMObjectToJsval(JSContext *cx, qsObjectHelper &aHelper,
   1.796 +                         const nsIID *iid, XPCNativeInterface **iface,
   1.797 +                         MutableHandleValue rval)
   1.798 +{
   1.799 +    NS_PRECONDITION(iface, "Who did that and why?");
   1.800 +
   1.801 +    // From the T_INTERFACE case in XPCConvert::NativeData2JS.
   1.802 +    // This is one of the slowest things quick stubs do.
   1.803 +
   1.804 +    nsresult rv;
   1.805 +    if (!XPCConvert::NativeInterface2JSObject(rval, nullptr,
   1.806 +                                              aHelper, iid, iface,
   1.807 +                                              true, &rv)) {
   1.808 +        // I can't tell if NativeInterface2JSObject throws JS exceptions
   1.809 +        // or not.  This is a sloppy stab at the right semantics; the
   1.810 +        // method really ought to be fixed to behave consistently.
   1.811 +        if (!JS_IsExceptionPending(cx))
   1.812 +            xpc_qsThrow(cx, NS_FAILED(rv) ? rv : NS_ERROR_UNEXPECTED);
   1.813 +        return false;
   1.814 +    }
   1.815 +
   1.816 +#ifdef DEBUG
   1.817 +    JSObject* jsobj = rval.toObjectOrNull();
   1.818 +    if (jsobj && !js::GetObjectParent(jsobj))
   1.819 +        MOZ_ASSERT(js::GetObjectClass(jsobj)->flags & JSCLASS_IS_GLOBAL,
   1.820 +                   "Why did we recreate this wrapper?");
   1.821 +#endif
   1.822 +
   1.823 +    return true;
   1.824 +}
   1.825 +
   1.826 +bool
   1.827 +xpc_qsVariantToJsval(JSContext *aCx,
   1.828 +                     nsIVariant *p,
   1.829 +                     MutableHandleValue rval)
   1.830 +{
   1.831 +    // From the T_INTERFACE case in XPCConvert::NativeData2JS.
   1.832 +    // Error handling is in XPCWrappedNative::CallMethod.
   1.833 +    if (p) {
   1.834 +        nsresult rv;
   1.835 +        bool ok = XPCVariant::VariantDataToJS(p, &rv, rval);
   1.836 +        if (!ok)
   1.837 +            xpc_qsThrow(aCx, rv);
   1.838 +        return ok;
   1.839 +    }
   1.840 +    rval.setNull();
   1.841 +    return true;
   1.842 +}
   1.843 +
   1.844 +#ifdef DEBUG
   1.845 +void
   1.846 +xpc_qsAssertContextOK(JSContext *cx)
   1.847 +{
   1.848 +    XPCJSContextStack* stack = XPCJSRuntime::Get()->GetJSContextStack();
   1.849 +
   1.850 +    JSContext *topJSContext = stack->Peek();
   1.851 +
   1.852 +    // This is what we're actually trying to assert here.
   1.853 +    MOZ_ASSERT(cx == topJSContext, "wrong context on XPCJSContextStack!");
   1.854 +}
   1.855 +#endif

mercurial