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