michael@0: /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ michael@0: /* vim: set ts=8 sts=4 et sw=4 tw=99: */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include "jsapi.h" michael@0: #include "jsfriendapi.h" michael@0: #include "jsprf.h" michael@0: #include "nsCOMPtr.h" michael@0: #include "AccessCheck.h" michael@0: #include "WrapperFactory.h" michael@0: #include "xpcprivate.h" michael@0: #include "XPCInlines.h" michael@0: #include "XPCQuickStubs.h" michael@0: #include "mozilla/dom/BindingUtils.h" michael@0: #include "mozilla/dom/Exceptions.h" michael@0: michael@0: using namespace mozilla; michael@0: using namespace JS; michael@0: michael@0: extern const char* xpc_qsStringTable; michael@0: michael@0: static const xpc_qsHashEntry * michael@0: LookupEntry(uint32_t tableSize, const xpc_qsHashEntry *table, const nsID &iid) michael@0: { michael@0: size_t i; michael@0: const xpc_qsHashEntry *p; michael@0: michael@0: i = iid.m0 % tableSize; michael@0: do michael@0: { michael@0: p = table + i; michael@0: if (p->iid.Equals(iid)) michael@0: return p; michael@0: i = p->chain; michael@0: } while (i != XPC_QS_NULL_INDEX); michael@0: return nullptr; michael@0: } michael@0: michael@0: static const xpc_qsHashEntry * michael@0: LookupInterfaceOrAncestor(uint32_t tableSize, const xpc_qsHashEntry *table, michael@0: const nsID &iid) michael@0: { michael@0: const xpc_qsHashEntry *entry = LookupEntry(tableSize, table, iid); michael@0: if (!entry) { michael@0: /* michael@0: * On a miss, we have to search for every interface the object michael@0: * supports, including ancestors. michael@0: */ michael@0: nsCOMPtr info; michael@0: if (NS_FAILED(nsXPConnect::XPConnect()->GetInfoForIID(&iid, getter_AddRefs(info)))) michael@0: return nullptr; michael@0: michael@0: const nsIID *piid; michael@0: for (;;) { michael@0: nsCOMPtr parent; michael@0: if (NS_FAILED(info->GetParent(getter_AddRefs(parent))) || michael@0: !parent || michael@0: NS_FAILED(parent->GetIIDShared(&piid))) { michael@0: break; michael@0: } michael@0: entry = LookupEntry(tableSize, table, *piid); michael@0: if (entry) michael@0: break; michael@0: info.swap(parent); michael@0: } michael@0: } michael@0: return entry; michael@0: } michael@0: michael@0: static MOZ_ALWAYS_INLINE bool michael@0: HasBitInInterfacesBitmap(JSObject *obj, uint32_t interfaceBit) michael@0: { michael@0: MOZ_ASSERT(IS_WN_REFLECTOR(obj), "Not a wrapper?"); michael@0: michael@0: const XPCWrappedNativeJSClass *clasp = michael@0: (const XPCWrappedNativeJSClass*)js::GetObjectClass(obj); michael@0: return (clasp->interfacesBitmap & (1 << interfaceBit)) != 0; michael@0: } michael@0: michael@0: static void michael@0: PointerFinalize(JSFreeOp *fop, JSObject *obj) michael@0: { michael@0: JSPropertyOp *popp = static_cast(JS_GetPrivate(obj)); michael@0: delete popp; michael@0: } michael@0: michael@0: const JSClass michael@0: PointerHolderClass = { michael@0: "Pointer", JSCLASS_HAS_PRIVATE, michael@0: JS_PropertyStub, JS_DeletePropertyStub, JS_PropertyStub, JS_StrictPropertyStub, michael@0: JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, PointerFinalize michael@0: }; michael@0: michael@0: bool michael@0: xpc_qsDefineQuickStubs(JSContext *cx, JSObject *protoArg, unsigned flags, michael@0: uint32_t ifacec, const nsIID **interfaces, michael@0: uint32_t tableSize, const xpc_qsHashEntry *table, michael@0: const xpc_qsPropertySpec *propspecs, michael@0: const xpc_qsFunctionSpec *funcspecs, michael@0: const char *stringTable) michael@0: { michael@0: /* michael@0: * Walk interfaces in reverse order to behave like XPConnect when a michael@0: * feature is defined in more than one of the interfaces. michael@0: * michael@0: * XPCNativeSet::FindMethod returns the first matching feature it finds, michael@0: * searching the interfaces forward. Here, definitions toward the michael@0: * front of 'interfaces' overwrite those toward the back. michael@0: */ michael@0: RootedObject proto(cx, protoArg); michael@0: for (uint32_t i = ifacec; i-- != 0;) { michael@0: const nsID &iid = *interfaces[i]; michael@0: const xpc_qsHashEntry *entry = michael@0: LookupInterfaceOrAncestor(tableSize, table, iid); michael@0: michael@0: if (entry) { michael@0: for (;;) { michael@0: // Define quick stubs for attributes. michael@0: const xpc_qsPropertySpec *ps = propspecs + entry->prop_index; michael@0: const xpc_qsPropertySpec *ps_end = ps + entry->n_props; michael@0: for ( ; ps < ps_end; ++ps) { michael@0: if (!JS_DefineProperty(cx, proto, michael@0: stringTable + ps->name_index, michael@0: JS::UndefinedHandleValue, michael@0: flags | JSPROP_SHARED | JSPROP_NATIVE_ACCESSORS, michael@0: (JSPropertyOp)ps->getter, michael@0: (JSStrictPropertyOp)ps->setter)) michael@0: return false; michael@0: } michael@0: michael@0: // Define quick stubs for methods. michael@0: const xpc_qsFunctionSpec *fs = funcspecs + entry->func_index; michael@0: const xpc_qsFunctionSpec *fs_end = fs + entry->n_funcs; michael@0: for ( ; fs < fs_end; ++fs) { michael@0: if (!JS_DefineFunction(cx, proto, michael@0: stringTable + fs->name_index, michael@0: reinterpret_cast(fs->native), michael@0: fs->arity, flags)) michael@0: return false; michael@0: } michael@0: michael@0: if (entry->newBindingProperties) { michael@0: if (entry->newBindingProperties->regular) { michael@0: mozilla::dom::DefineWebIDLBindingPropertiesOnXPCObject(cx, proto, entry->newBindingProperties->regular, false); michael@0: } michael@0: if (entry->newBindingProperties->chromeOnly && michael@0: xpc::AccessCheck::isChrome(js::GetContextCompartment(cx))) { michael@0: mozilla::dom::DefineWebIDLBindingPropertiesOnXPCObject(cx, proto, entry->newBindingProperties->chromeOnly, false); michael@0: } michael@0: } michael@0: // Next. michael@0: size_t j = entry->parentInterface; michael@0: if (j == XPC_QS_NULL_INDEX) michael@0: break; michael@0: entry = table + j; michael@0: } michael@0: } michael@0: } michael@0: michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: xpc_qsThrow(JSContext *cx, nsresult rv) michael@0: { michael@0: XPCThrower::Throw(rv, cx); michael@0: return false; michael@0: } michael@0: michael@0: /** michael@0: * Get the interface name and member name (for error messages). michael@0: * michael@0: * We could instead have each quick stub pass its name to the error-handling michael@0: * functions, as that name is statically known. But that would be redundant; michael@0: * the information is handy at runtime anyway. Also, this code often produces michael@0: * a more specific error message, e.g. "[nsIDOMHTMLDocument.appendChild]" michael@0: * rather than "[nsIDOMNode.appendChild]". michael@0: */ michael@0: static void michael@0: GetMemberInfo(JSObject *obj, jsid memberId, const char **ifaceName) michael@0: { michael@0: *ifaceName = "Unknown"; michael@0: michael@0: // Don't try to generate a useful name if there are security wrappers, michael@0: // because it isn't worth the risk of something going wrong just to generate michael@0: // an error message. Instead, only handle the simple case where we have the michael@0: // reflector in hand. michael@0: if (IS_WN_REFLECTOR(obj)) { michael@0: XPCWrappedNative *wrapper = XPCWrappedNative::Get(obj); michael@0: XPCWrappedNativeProto *proto = wrapper->GetProto(); michael@0: if (proto) { michael@0: XPCNativeSet *set = proto->GetSet(); michael@0: if (set) { michael@0: XPCNativeMember *member; michael@0: XPCNativeInterface *iface; michael@0: michael@0: if (set->FindMember(memberId, &member, &iface)) michael@0: *ifaceName = iface->GetNameString(); michael@0: } michael@0: } michael@0: } michael@0: } michael@0: michael@0: static void michael@0: GetMethodInfo(JSContext *cx, jsval *vp, const char **ifaceNamep, jsid *memberIdp) michael@0: { michael@0: CallReceiver call = CallReceiverFromVp(vp); michael@0: RootedObject funobj(cx, &call.callee()); michael@0: MOZ_ASSERT(JS_ObjectIsFunction(cx, funobj), michael@0: "JSNative callee should be Function object"); michael@0: RootedString str(cx, JS_GetFunctionId(JS_GetObjectFunction(funobj))); michael@0: RootedId methodId(cx, str ? INTERNED_STRING_TO_JSID(cx, str) : JSID_VOID); michael@0: GetMemberInfo(&call.thisv().toObject(), methodId, ifaceNamep); michael@0: *memberIdp = methodId; michael@0: } michael@0: michael@0: static bool michael@0: ThrowCallFailed(JSContext *cx, nsresult rv, michael@0: const char *ifaceName, HandleId memberId, const char *memberName) michael@0: { michael@0: /* Only one of memberId or memberName should be given. */ michael@0: MOZ_ASSERT(JSID_IS_VOID(memberId) != !memberName); michael@0: michael@0: // From XPCThrower::ThrowBadResult. michael@0: char* sz; michael@0: const char* format; michael@0: const char* name; michael@0: michael@0: // If the cx already has a pending exception, just throw that. michael@0: // michael@0: // We used to check here to make sure the exception matched rv (whatever michael@0: // that means). But this meant that we'd be calling into JSAPI below with michael@0: // a pending exception, which isn't really kosher. The first exception thrown michael@0: // should generally take precedence anyway. michael@0: if (JS_IsExceptionPending(cx)) michael@0: return false; michael@0: michael@0: // else... michael@0: michael@0: if (!nsXPCException::NameAndFormatForNSResult(NS_ERROR_XPC_NATIVE_RETURNED_FAILURE, nullptr, &format) || michael@0: !format) { michael@0: format = ""; michael@0: } michael@0: michael@0: JSAutoByteString memberNameBytes; michael@0: if (!memberName) { michael@0: memberName = JSID_IS_STRING(memberId) michael@0: ? memberNameBytes.encodeLatin1(cx, JSID_TO_STRING(memberId)) michael@0: : "unknown"; michael@0: } michael@0: if (nsXPCException::NameAndFormatForNSResult(rv, &name, nullptr) michael@0: && name) { michael@0: sz = JS_smprintf("%s 0x%x (%s) [%s.%s]", michael@0: format, rv, name, ifaceName, memberName); michael@0: } else { michael@0: sz = JS_smprintf("%s 0x%x [%s.%s]", michael@0: format, rv, ifaceName, memberName); michael@0: } michael@0: michael@0: dom::Throw(cx, rv, sz); michael@0: michael@0: if (sz) michael@0: JS_smprintf_free(sz); michael@0: michael@0: return false; michael@0: } michael@0: michael@0: bool michael@0: xpc_qsThrowGetterSetterFailed(JSContext *cx, nsresult rv, JSObject *obj, michael@0: jsid memberIdArg) michael@0: { michael@0: RootedId memberId(cx, memberIdArg); michael@0: const char *ifaceName; michael@0: GetMemberInfo(obj, memberId, &ifaceName); michael@0: return ThrowCallFailed(cx, rv, ifaceName, memberId, nullptr); michael@0: } michael@0: michael@0: bool michael@0: xpc_qsThrowGetterSetterFailed(JSContext *cx, nsresult rv, JSObject *objArg, michael@0: const char* memberName) michael@0: { michael@0: RootedObject obj(cx, objArg); michael@0: JSString *str = JS_InternString(cx, memberName); michael@0: if (!str) { michael@0: return false; michael@0: } michael@0: return xpc_qsThrowGetterSetterFailed(cx, rv, obj, michael@0: INTERNED_STRING_TO_JSID(cx, str)); michael@0: } michael@0: michael@0: bool michael@0: xpc_qsThrowGetterSetterFailed(JSContext *cx, nsresult rv, JSObject *obj, michael@0: uint16_t memberIndex) michael@0: { michael@0: return xpc_qsThrowGetterSetterFailed(cx, rv, obj, michael@0: xpc_qsStringTable + memberIndex); michael@0: } michael@0: michael@0: bool michael@0: xpc_qsThrowMethodFailed(JSContext *cx, nsresult rv, jsval *vp) michael@0: { michael@0: const char *ifaceName; michael@0: RootedId memberId(cx); michael@0: GetMethodInfo(cx, vp, &ifaceName, memberId.address()); michael@0: return ThrowCallFailed(cx, rv, ifaceName, memberId, nullptr); michael@0: } michael@0: michael@0: static void michael@0: ThrowBadArg(JSContext *cx, nsresult rv, const char *ifaceName, michael@0: jsid memberId, const char *memberName, unsigned paramnum) michael@0: { michael@0: /* Only one memberId or memberName should be given. */ michael@0: MOZ_ASSERT(JSID_IS_VOID(memberId) != !memberName); michael@0: michael@0: // From XPCThrower::ThrowBadParam. michael@0: char* sz; michael@0: const char* format; michael@0: michael@0: if (!nsXPCException::NameAndFormatForNSResult(rv, nullptr, &format)) michael@0: format = ""; michael@0: michael@0: JSAutoByteString memberNameBytes; michael@0: if (!memberName) { michael@0: memberName = JSID_IS_STRING(memberId) michael@0: ? memberNameBytes.encodeLatin1(cx, JSID_TO_STRING(memberId)) michael@0: : "unknown"; michael@0: } michael@0: sz = JS_smprintf("%s arg %u [%s.%s]", michael@0: format, (unsigned int) paramnum, ifaceName, memberName); michael@0: michael@0: dom::Throw(cx, rv, sz); michael@0: michael@0: if (sz) michael@0: JS_smprintf_free(sz); michael@0: } michael@0: michael@0: void michael@0: xpc_qsThrowBadArg(JSContext *cx, nsresult rv, jsval *vp, unsigned paramnum) michael@0: { michael@0: const char *ifaceName; michael@0: RootedId memberId(cx); michael@0: GetMethodInfo(cx, vp, &ifaceName, memberId.address()); michael@0: ThrowBadArg(cx, rv, ifaceName, memberId, nullptr, paramnum); michael@0: } michael@0: michael@0: void michael@0: xpc_qsThrowBadArgWithCcx(XPCCallContext &ccx, nsresult rv, unsigned paramnum) michael@0: { michael@0: XPCThrower::ThrowBadParam(rv, paramnum, ccx); michael@0: } michael@0: michael@0: void michael@0: xpc_qsThrowBadArgWithDetails(JSContext *cx, nsresult rv, unsigned paramnum, michael@0: const char *ifaceName, const char *memberName) michael@0: { michael@0: ThrowBadArg(cx, rv, ifaceName, JSID_VOID, memberName, paramnum); michael@0: } michael@0: michael@0: void michael@0: xpc_qsThrowBadSetterValue(JSContext *cx, nsresult rv, michael@0: JSObject *obj, jsid propIdArg) michael@0: { michael@0: RootedId propId(cx, propIdArg); michael@0: const char *ifaceName; michael@0: GetMemberInfo(obj, propId, &ifaceName); michael@0: ThrowBadArg(cx, rv, ifaceName, propId, nullptr, 0); michael@0: } michael@0: michael@0: void michael@0: xpc_qsThrowBadSetterValue(JSContext *cx, nsresult rv, michael@0: JSObject *objArg, const char* propName) michael@0: { michael@0: RootedObject obj(cx, objArg); michael@0: JSString *str = JS_InternString(cx, propName); michael@0: if (!str) { michael@0: return; michael@0: } michael@0: xpc_qsThrowBadSetterValue(cx, rv, obj, INTERNED_STRING_TO_JSID(cx, str)); michael@0: } michael@0: michael@0: void michael@0: xpc_qsThrowBadSetterValue(JSContext *cx, nsresult rv, JSObject *obj, michael@0: uint16_t name_index) michael@0: { michael@0: xpc_qsThrowBadSetterValue(cx, rv, obj, xpc_qsStringTable + name_index); michael@0: } michael@0: michael@0: bool michael@0: xpc_qsGetterOnlyPropertyStub(JSContext *cx, HandleObject obj, HandleId id, bool strict, michael@0: MutableHandleValue vp) michael@0: { michael@0: return JS_ReportErrorFlagsAndNumber(cx, michael@0: JSREPORT_WARNING | JSREPORT_STRICT | michael@0: JSREPORT_STRICT_MODE_ERROR, michael@0: js_GetErrorMessage, nullptr, michael@0: JSMSG_GETTER_ONLY); michael@0: } michael@0: michael@0: bool michael@0: xpc_qsGetterOnlyNativeStub(JSContext *cx, unsigned argc, jsval *vp) michael@0: { michael@0: return JS_ReportErrorFlagsAndNumber(cx, michael@0: JSREPORT_WARNING | JSREPORT_STRICT | michael@0: JSREPORT_STRICT_MODE_ERROR, michael@0: js_GetErrorMessage, nullptr, michael@0: JSMSG_GETTER_ONLY); michael@0: } michael@0: michael@0: xpc_qsDOMString::xpc_qsDOMString(JSContext *cx, HandleValue v, michael@0: MutableHandleValue pval, bool notpassed, michael@0: StringificationBehavior nullBehavior, michael@0: StringificationBehavior undefinedBehavior) michael@0: { michael@0: typedef implementation_type::char_traits traits; michael@0: // From the T_DOMSTRING case in XPCConvert::JSData2Native. michael@0: JSString *s = InitOrStringify(cx, v, michael@0: pval, notpassed, michael@0: nullBehavior, michael@0: undefinedBehavior); michael@0: if (!s) michael@0: return; michael@0: michael@0: size_t len; michael@0: const jschar *chars = JS_GetStringCharsZAndLength(cx, s, &len); michael@0: if (!chars) { michael@0: mValid = false; michael@0: return; michael@0: } michael@0: michael@0: new(mBuf) implementation_type(chars, len); michael@0: mValid = true; michael@0: } michael@0: michael@0: xpc_qsACString::xpc_qsACString(JSContext *cx, HandleValue v, michael@0: MutableHandleValue pval, bool notpassed, michael@0: StringificationBehavior nullBehavior, michael@0: StringificationBehavior undefinedBehavior) michael@0: { michael@0: typedef implementation_type::char_traits traits; michael@0: // From the T_CSTRING case in XPCConvert::JSData2Native. michael@0: JSString *s = InitOrStringify(cx, v, michael@0: pval, notpassed, michael@0: nullBehavior, michael@0: undefinedBehavior); michael@0: if (!s) michael@0: return; michael@0: michael@0: size_t len = JS_GetStringEncodingLength(cx, s); michael@0: if (len == size_t(-1)) { michael@0: mValid = false; michael@0: return; michael@0: } michael@0: michael@0: JSAutoByteString bytes(cx, s); michael@0: if (!bytes) { michael@0: mValid = false; michael@0: return; michael@0: } michael@0: michael@0: new(mBuf) implementation_type(bytes.ptr(), len); michael@0: mValid = true; michael@0: } michael@0: michael@0: xpc_qsAUTF8String::xpc_qsAUTF8String(JSContext *cx, HandleValue v, MutableHandleValue pval, bool notpassed) michael@0: { michael@0: typedef nsCharTraits traits; michael@0: // From the T_UTF8STRING case in XPCConvert::JSData2Native. michael@0: JSString *s = InitOrStringify(cx, v, pval, notpassed, eNull, eNull); michael@0: if (!s) michael@0: return; michael@0: michael@0: size_t len; michael@0: const char16_t *chars = JS_GetStringCharsZAndLength(cx, s, &len); michael@0: if (!chars) { michael@0: mValid = false; michael@0: return; michael@0: } michael@0: michael@0: new(mBuf) implementation_type(chars, len); michael@0: mValid = true; michael@0: } michael@0: michael@0: static nsresult michael@0: getNative(nsISupports *idobj, michael@0: HandleObject obj, michael@0: const nsIID &iid, michael@0: void **ppThis, michael@0: nsISupports **pThisRef, michael@0: jsval *vp) michael@0: { michael@0: nsresult rv = idobj->QueryInterface(iid, ppThis); michael@0: *pThisRef = static_cast(*ppThis); michael@0: if (NS_SUCCEEDED(rv)) michael@0: *vp = OBJECT_TO_JSVAL(obj); michael@0: return rv; michael@0: } michael@0: michael@0: static inline nsresult michael@0: getNativeFromWrapper(JSContext *cx, michael@0: XPCWrappedNative *wrapper, michael@0: const nsIID &iid, michael@0: void **ppThis, michael@0: nsISupports **pThisRef, michael@0: jsval *vp) michael@0: { michael@0: RootedObject obj(cx, wrapper->GetFlatJSObject()); michael@0: return getNative(wrapper->GetIdentityObject(), obj, iid, ppThis, pThisRef, michael@0: vp); michael@0: } michael@0: michael@0: michael@0: nsresult michael@0: getWrapper(JSContext *cx, michael@0: JSObject *obj, michael@0: XPCWrappedNative **wrapper, michael@0: JSObject **cur, michael@0: XPCWrappedNativeTearOff **tearoff) michael@0: { michael@0: // We can have at most three layers in need of unwrapping here: michael@0: // * A (possible) security wrapper michael@0: // * A (possible) Xray waiver michael@0: // * A (possible) outer window michael@0: // michael@0: // If we pass stopAtOuter == false, we can handle all three with one call michael@0: // to js::CheckedUnwrap. michael@0: if (js::IsWrapper(obj)) { michael@0: JSObject* inner = js::CheckedUnwrap(obj, /* stopAtOuter = */ false); michael@0: michael@0: // Hack - For historical reasons, wrapped chrome JS objects have been michael@0: // passable as native interfaces. We'd like to fix this, but it michael@0: // involves fixing the contacts API and PeerConnection to stop using michael@0: // COWs. This needs to happen, but for now just preserve the old michael@0: // behavior. michael@0: // michael@0: // Note that there is an identical hack in michael@0: // XPCConvert::JSObject2NativeInterface which should be removed if this michael@0: // one is. michael@0: if (!inner && MOZ_UNLIKELY(xpc::WrapperFactory::IsCOW(obj))) michael@0: inner = js::UncheckedUnwrap(obj); michael@0: michael@0: // The safe unwrap might have failed if we encountered an object that michael@0: // we're not allowed to unwrap. If it didn't fail though, we should be michael@0: // done with wrappers. michael@0: if (!inner) michael@0: return NS_ERROR_XPC_SECURITY_MANAGER_VETO; michael@0: MOZ_ASSERT(!js::IsWrapper(inner)); michael@0: michael@0: obj = inner; michael@0: } michael@0: michael@0: // Start with sane values. michael@0: *wrapper = nullptr; michael@0: *cur = nullptr; michael@0: *tearoff = nullptr; michael@0: michael@0: if (dom::IsDOMObject(obj)) { michael@0: *cur = obj; michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: // Handle tearoffs. michael@0: // michael@0: // If |obj| is of the tearoff class, that means we're dealing with a JS michael@0: // object reflection of a particular interface (ie, |foo.nsIBar|). These michael@0: // JS objects are parented to their wrapper, so we snag the tearoff object michael@0: // along the way (if desired), and then set |obj| to its parent. michael@0: const js::Class* clasp = js::GetObjectClass(obj); michael@0: if (clasp == &XPC_WN_Tearoff_JSClass) { michael@0: *tearoff = (XPCWrappedNativeTearOff*) js::GetObjectPrivate(obj); michael@0: obj = js::GetObjectParent(obj); michael@0: } michael@0: michael@0: // If we've got a WN, store things the way callers expect. Otherwise, leave michael@0: // things null and return. michael@0: if (IS_WN_CLASS(clasp)) michael@0: *wrapper = XPCWrappedNative::Get(obj); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: castNative(JSContext *cx, michael@0: XPCWrappedNative *wrapper, michael@0: JSObject *curArg, michael@0: XPCWrappedNativeTearOff *tearoff, michael@0: const nsIID &iid, michael@0: void **ppThis, michael@0: nsISupports **pThisRef, michael@0: MutableHandleValue vp) michael@0: { michael@0: RootedObject cur(cx, curArg); michael@0: if (wrapper) { michael@0: nsresult rv = getNativeFromWrapper(cx,wrapper, iid, ppThis, pThisRef, michael@0: vp.address()); michael@0: michael@0: if (rv != NS_ERROR_NO_INTERFACE) michael@0: return rv; michael@0: } else if (cur) { michael@0: nsISupports *native; michael@0: if (!(native = mozilla::dom::UnwrapDOMObjectToISupports(cur))) { michael@0: *pThisRef = nullptr; michael@0: return NS_ERROR_ILLEGAL_VALUE; michael@0: } michael@0: michael@0: if (NS_SUCCEEDED(getNative(native, cur, iid, ppThis, pThisRef, vp.address()))) { michael@0: return NS_OK; michael@0: } michael@0: } michael@0: michael@0: *pThisRef = nullptr; michael@0: return NS_ERROR_XPC_BAD_OP_ON_WN_PROTO; michael@0: } michael@0: michael@0: nsISupports* michael@0: castNativeFromWrapper(JSContext *cx, michael@0: JSObject *obj, michael@0: uint32_t interfaceBit, michael@0: uint32_t protoID, michael@0: int32_t protoDepth, michael@0: nsISupports **pRef, michael@0: MutableHandleValue pVal, michael@0: nsresult *rv) michael@0: { michael@0: XPCWrappedNative *wrapper; michael@0: XPCWrappedNativeTearOff *tearoff; michael@0: JSObject *cur; michael@0: michael@0: if (IS_WN_REFLECTOR(obj)) { michael@0: cur = obj; michael@0: wrapper = XPCWrappedNative::Get(obj); michael@0: tearoff = nullptr; michael@0: } else { michael@0: *rv = getWrapper(cx, obj, &wrapper, &cur, &tearoff); michael@0: if (NS_FAILED(*rv)) michael@0: return nullptr; michael@0: } michael@0: michael@0: nsISupports *native; michael@0: if (wrapper) { michael@0: native = wrapper->GetIdentityObject(); michael@0: cur = wrapper->GetFlatJSObject(); michael@0: if (!native || !HasBitInInterfacesBitmap(cur, interfaceBit)) { michael@0: native = nullptr; michael@0: } michael@0: } else if (cur && protoDepth >= 0) { michael@0: const mozilla::dom::DOMClass* domClass = michael@0: mozilla::dom::GetDOMClass(cur); michael@0: native = mozilla::dom::UnwrapDOMObject(cur); michael@0: if (native && michael@0: (uint32_t)domClass->mInterfaceChain[protoDepth] != protoID) { michael@0: native = nullptr; michael@0: } michael@0: } else { michael@0: native = nullptr; michael@0: } michael@0: michael@0: if (native) { michael@0: *pRef = nullptr; michael@0: pVal.setObjectOrNull(cur); michael@0: *rv = NS_OK; michael@0: } else { michael@0: *rv = NS_ERROR_XPC_BAD_CONVERT_JS; michael@0: } michael@0: michael@0: return native; michael@0: } michael@0: michael@0: bool michael@0: xpc_qsUnwrapThisFromCcxImpl(XPCCallContext &ccx, michael@0: const nsIID &iid, michael@0: void **ppThis, michael@0: nsISupports **pThisRef, michael@0: jsval *vp) michael@0: { michael@0: nsISupports *native = ccx.GetIdentityObject(); michael@0: if (!native) michael@0: return xpc_qsThrow(ccx.GetJSContext(), NS_ERROR_XPC_HAS_BEEN_SHUTDOWN); michael@0: michael@0: RootedObject obj(ccx, ccx.GetFlattenedJSObject()); michael@0: nsresult rv = getNative(native, obj, iid, ppThis, pThisRef, vp); michael@0: if (NS_FAILED(rv)) michael@0: return xpc_qsThrow(ccx.GetJSContext(), rv); michael@0: return true; michael@0: } michael@0: michael@0: nsresult michael@0: xpc_qsUnwrapArgImpl(JSContext *cx, michael@0: HandleValue v, michael@0: const nsIID &iid, michael@0: void **ppArg, michael@0: nsISupports **ppArgRef, michael@0: MutableHandleValue vp) michael@0: { michael@0: nsresult rv; michael@0: RootedObject src(cx, xpc_qsUnwrapObj(v, ppArgRef, &rv)); michael@0: if (!src) { michael@0: *ppArg = nullptr; michael@0: michael@0: return rv; michael@0: } michael@0: michael@0: XPCWrappedNative *wrapper; michael@0: XPCWrappedNativeTearOff *tearoff; michael@0: JSObject *obj2; michael@0: rv = getWrapper(cx, src, &wrapper, &obj2, &tearoff); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: if (wrapper || obj2) { michael@0: if (NS_FAILED(castNative(cx, wrapper, obj2, tearoff, iid, ppArg, michael@0: ppArgRef, vp))) michael@0: return NS_ERROR_XPC_BAD_CONVERT_JS; michael@0: return NS_OK; michael@0: } michael@0: // else... michael@0: // Slow path. michael@0: michael@0: // Try to unwrap a slim wrapper. michael@0: nsISupports *iface; michael@0: if (XPCConvert::GetISupportsFromJSObject(src, &iface)) { michael@0: if (!iface || NS_FAILED(iface->QueryInterface(iid, ppArg))) { michael@0: *ppArgRef = nullptr; michael@0: return NS_ERROR_XPC_BAD_CONVERT_JS; michael@0: } michael@0: michael@0: *ppArgRef = static_cast(*ppArg); michael@0: return NS_OK; michael@0: } michael@0: michael@0: // Create the ccx needed for quick stubs. michael@0: XPCCallContext ccx(JS_CALLER, cx); michael@0: if (!ccx.IsValid()) { michael@0: *ppArgRef = nullptr; michael@0: return NS_ERROR_XPC_BAD_CONVERT_JS; michael@0: } michael@0: michael@0: nsRefPtr wrappedJS; michael@0: rv = nsXPCWrappedJS::GetNewOrUsed(src, iid, getter_AddRefs(wrappedJS)); michael@0: if (NS_FAILED(rv) || !wrappedJS) { michael@0: *ppArgRef = nullptr; michael@0: return rv; michael@0: } michael@0: michael@0: // We need to go through the QueryInterface logic to make this return michael@0: // the right thing for the various 'special' interfaces; e.g. michael@0: // nsIPropertyBag. We must use AggregatedQueryInterface in cases where michael@0: // there is an outer to avoid nasty recursion. michael@0: rv = wrappedJS->QueryInterface(iid, ppArg); michael@0: if (NS_SUCCEEDED(rv)) { michael@0: *ppArgRef = static_cast(*ppArg); michael@0: vp.setObjectOrNull(wrappedJS->GetJSObject()); michael@0: } michael@0: return rv; michael@0: } michael@0: michael@0: bool michael@0: xpc_qsJsvalToCharStr(JSContext *cx, HandleValue v, JSAutoByteString *bytes) michael@0: { michael@0: MOZ_ASSERT(!bytes->ptr()); michael@0: michael@0: if (v.isNullOrUndefined()) michael@0: return true; michael@0: michael@0: JSString *str = ToString(cx, v); michael@0: if (!str) michael@0: return false; michael@0: return !!bytes->encodeLatin1(cx, str); michael@0: } michael@0: michael@0: namespace xpc { michael@0: michael@0: bool michael@0: NonVoidStringToJsval(JSContext *cx, nsAString &str, MutableHandleValue rval) michael@0: { michael@0: nsStringBuffer* sharedBuffer; michael@0: if (!XPCStringConvert::ReadableToJSVal(cx, str, &sharedBuffer, rval)) michael@0: return false; michael@0: michael@0: if (sharedBuffer) { michael@0: // The string was shared but ReadableToJSVal didn't addref it. michael@0: // Move the ownership from str to jsstr. michael@0: str.ForgetSharedBuffer(); michael@0: } michael@0: return true; michael@0: } michael@0: michael@0: } // namespace xpc michael@0: michael@0: bool michael@0: xpc_qsXPCOMObjectToJsval(JSContext *cx, qsObjectHelper &aHelper, michael@0: const nsIID *iid, XPCNativeInterface **iface, michael@0: MutableHandleValue rval) michael@0: { michael@0: NS_PRECONDITION(iface, "Who did that and why?"); michael@0: michael@0: // From the T_INTERFACE case in XPCConvert::NativeData2JS. michael@0: // This is one of the slowest things quick stubs do. michael@0: michael@0: nsresult rv; michael@0: if (!XPCConvert::NativeInterface2JSObject(rval, nullptr, michael@0: aHelper, iid, iface, michael@0: true, &rv)) { michael@0: // I can't tell if NativeInterface2JSObject throws JS exceptions michael@0: // or not. This is a sloppy stab at the right semantics; the michael@0: // method really ought to be fixed to behave consistently. michael@0: if (!JS_IsExceptionPending(cx)) michael@0: xpc_qsThrow(cx, NS_FAILED(rv) ? rv : NS_ERROR_UNEXPECTED); michael@0: return false; michael@0: } michael@0: michael@0: #ifdef DEBUG michael@0: JSObject* jsobj = rval.toObjectOrNull(); michael@0: if (jsobj && !js::GetObjectParent(jsobj)) michael@0: MOZ_ASSERT(js::GetObjectClass(jsobj)->flags & JSCLASS_IS_GLOBAL, michael@0: "Why did we recreate this wrapper?"); michael@0: #endif michael@0: michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: xpc_qsVariantToJsval(JSContext *aCx, michael@0: nsIVariant *p, michael@0: MutableHandleValue rval) michael@0: { michael@0: // From the T_INTERFACE case in XPCConvert::NativeData2JS. michael@0: // Error handling is in XPCWrappedNative::CallMethod. michael@0: if (p) { michael@0: nsresult rv; michael@0: bool ok = XPCVariant::VariantDataToJS(p, &rv, rval); michael@0: if (!ok) michael@0: xpc_qsThrow(aCx, rv); michael@0: return ok; michael@0: } michael@0: rval.setNull(); michael@0: return true; michael@0: } michael@0: michael@0: #ifdef DEBUG michael@0: void michael@0: xpc_qsAssertContextOK(JSContext *cx) michael@0: { michael@0: XPCJSContextStack* stack = XPCJSRuntime::Get()->GetJSContextStack(); michael@0: michael@0: JSContext *topJSContext = stack->Peek(); michael@0: michael@0: // This is what we're actually trying to assert here. michael@0: MOZ_ASSERT(cx == topJSContext, "wrong context on XPCJSContextStack!"); michael@0: } michael@0: #endif