Bumping manifests a=b2g-bump
[gecko.git] / js / xpconnect / src / XPCQuickStubs.cpp
blob7d660b1a32891fb439dbc92e23242a9cfab05df0
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /* vim: set ts=8 sts=4 et sw=4 tw=99: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "jsapi.h"
8 #include "jsfriendapi.h"
9 #include "jsprf.h"
10 #include "nsCOMPtr.h"
11 #include "AccessCheck.h"
12 #include "WrapperFactory.h"
13 #include "xpcprivate.h"
14 #include "XPCInlines.h"
15 #include "XPCQuickStubs.h"
16 #include "mozilla/dom/BindingUtils.h"
17 #include "mozilla/dom/Exceptions.h"
19 using namespace mozilla;
20 using namespace JS;
22 extern const char* xpc_qsStringTable;
24 static const xpc_qsHashEntry*
25 LookupEntry(uint32_t tableSize, const xpc_qsHashEntry* table, const nsID& iid)
27 size_t i;
28 const xpc_qsHashEntry* p;
30 i = iid.m0 % tableSize;
33 p = table + i;
34 if (p->iid.Equals(iid))
35 return p;
36 i = p->chain;
37 } while (i != XPC_QS_NULL_INDEX);
38 return nullptr;
41 static const xpc_qsHashEntry*
42 LookupInterfaceOrAncestor(uint32_t tableSize, const xpc_qsHashEntry* table,
43 const nsID& iid)
45 const xpc_qsHashEntry* entry = LookupEntry(tableSize, table, iid);
46 if (!entry) {
48 * On a miss, we have to search for every interface the object
49 * supports, including ancestors.
51 nsCOMPtr<nsIInterfaceInfo> info;
52 if (NS_FAILED(nsXPConnect::XPConnect()->GetInfoForIID(&iid, getter_AddRefs(info))))
53 return nullptr;
55 const nsIID* piid;
56 for (;;) {
57 nsCOMPtr<nsIInterfaceInfo> parent;
58 if (NS_FAILED(info->GetParent(getter_AddRefs(parent))) ||
59 !parent ||
60 NS_FAILED(parent->GetIIDShared(&piid))) {
61 break;
63 entry = LookupEntry(tableSize, table, *piid);
64 if (entry)
65 break;
66 info.swap(parent);
69 return entry;
72 static MOZ_ALWAYS_INLINE bool
73 HasBitInInterfacesBitmap(JSObject* obj, uint32_t interfaceBit)
75 MOZ_ASSERT(IS_WN_REFLECTOR(obj), "Not a wrapper?");
77 const XPCWrappedNativeJSClass* clasp =
78 (const XPCWrappedNativeJSClass*)js::GetObjectClass(obj);
79 return (clasp->interfacesBitmap & (1 << interfaceBit)) != 0;
82 static void
83 PointerFinalize(JSFreeOp* fop, JSObject* obj)
85 JSPropertyOp* popp = static_cast<JSPropertyOp*>(JS_GetPrivate(obj));
86 delete popp;
89 const JSClass
90 PointerHolderClass = {
91 "Pointer", JSCLASS_HAS_PRIVATE,
92 JS_PropertyStub, JS_DeletePropertyStub, JS_PropertyStub, JS_StrictPropertyStub,
93 JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, PointerFinalize
96 bool
97 xpc_qsDefineQuickStubs(JSContext* cx, JSObject* protoArg, unsigned flags,
98 uint32_t ifacec, const nsIID** interfaces,
99 uint32_t tableSize, const xpc_qsHashEntry* table,
100 const xpc_qsPropertySpec* propspecs,
101 const xpc_qsFunctionSpec* funcspecs,
102 const char* stringTable)
105 * Walk interfaces in reverse order to behave like XPConnect when a
106 * feature is defined in more than one of the interfaces.
108 * XPCNativeSet::FindMethod returns the first matching feature it finds,
109 * searching the interfaces forward. Here, definitions toward the
110 * front of 'interfaces' overwrite those toward the back.
112 RootedObject proto(cx, protoArg);
113 for (uint32_t i = ifacec; i-- != 0;) {
114 const nsID& iid = *interfaces[i];
115 const xpc_qsHashEntry* entry =
116 LookupInterfaceOrAncestor(tableSize, table, iid);
118 if (entry) {
119 for (;;) {
120 // Define quick stubs for attributes.
121 const xpc_qsPropertySpec* ps = propspecs + entry->prop_index;
122 const xpc_qsPropertySpec* ps_end = ps + entry->n_props;
123 for ( ; ps < ps_end; ++ps) {
124 if (!JS_DefineProperty(cx, proto,
125 stringTable + ps->name_index,
126 JS::UndefinedHandleValue,
127 flags | JSPROP_SHARED | JSPROP_NATIVE_ACCESSORS,
128 (JSPropertyOp)ps->getter,
129 (JSStrictPropertyOp)ps->setter))
130 return false;
133 // Define quick stubs for methods.
134 const xpc_qsFunctionSpec* fs = funcspecs + entry->func_index;
135 const xpc_qsFunctionSpec* fs_end = fs + entry->n_funcs;
136 for ( ; fs < fs_end; ++fs) {
137 if (!JS_DefineFunction(cx, proto,
138 stringTable + fs->name_index,
139 reinterpret_cast<JSNative>(fs->native),
140 fs->arity, flags))
141 return false;
144 if (entry->newBindingProperties) {
145 if (entry->newBindingProperties->regular) {
146 mozilla::dom::DefineWebIDLBindingPropertiesOnXPCObject(cx, proto, entry->newBindingProperties->regular);
148 if (entry->newBindingProperties->chromeOnly &&
149 xpc::AccessCheck::isChrome(js::GetContextCompartment(cx))) {
150 mozilla::dom::DefineWebIDLBindingPropertiesOnXPCObject(cx, proto, entry->newBindingProperties->chromeOnly);
153 // Next.
154 size_t j = entry->parentInterface;
155 if (j == XPC_QS_NULL_INDEX)
156 break;
157 entry = table + j;
162 return true;
165 bool
166 xpc_qsThrow(JSContext* cx, nsresult rv)
168 XPCThrower::Throw(rv, cx);
169 return false;
173 * Get the interface name and member name (for error messages).
175 * We could instead have each quick stub pass its name to the error-handling
176 * functions, as that name is statically known. But that would be redundant;
177 * the information is handy at runtime anyway. Also, this code often produces
178 * a more specific error message, e.g. "[nsIDOMHTMLDocument.appendChild]"
179 * rather than "[nsIDOMNode.appendChild]".
181 static void
182 GetMemberInfo(JSObject* obj, jsid memberId, const char** ifaceName)
184 *ifaceName = "Unknown";
186 // Don't try to generate a useful name if there are security wrappers,
187 // because it isn't worth the risk of something going wrong just to generate
188 // an error message. Instead, only handle the simple case where we have the
189 // reflector in hand.
190 if (IS_WN_REFLECTOR(obj)) {
191 XPCWrappedNative* wrapper = XPCWrappedNative::Get(obj);
192 XPCWrappedNativeProto* proto = wrapper->GetProto();
193 if (proto) {
194 XPCNativeSet* set = proto->GetSet();
195 if (set) {
196 XPCNativeMember* member;
197 XPCNativeInterface* iface;
199 if (set->FindMember(memberId, &member, &iface))
200 *ifaceName = iface->GetNameString();
206 static void
207 GetMethodInfo(JSContext* cx, jsval* vp, const char** ifaceNamep, jsid* memberIdp)
209 CallReceiver call = CallReceiverFromVp(vp);
210 RootedObject funobj(cx, &call.callee());
211 MOZ_ASSERT(JS_ObjectIsFunction(cx, funobj),
212 "JSNative callee should be Function object");
213 RootedString str(cx, JS_GetFunctionId(JS_GetObjectFunction(funobj)));
214 RootedId methodId(cx, str ? INTERNED_STRING_TO_JSID(cx, str) : JSID_VOID);
215 GetMemberInfo(&call.thisv().toObject(), methodId, ifaceNamep);
216 *memberIdp = methodId;
219 static bool
220 ThrowCallFailed(JSContext* cx, nsresult rv,
221 const char* ifaceName, HandleId memberId, const char* memberName)
223 /* Only one of memberId or memberName should be given. */
224 MOZ_ASSERT(JSID_IS_VOID(memberId) != !memberName);
226 // From XPCThrower::ThrowBadResult.
227 char* sz;
228 const char* format;
229 const char* name;
231 // If the cx already has a pending exception, just throw that.
233 // We used to check here to make sure the exception matched rv (whatever
234 // that means). But this meant that we'd be calling into JSAPI below with
235 // a pending exception, which isn't really kosher. The first exception thrown
236 // should generally take precedence anyway.
237 if (JS_IsExceptionPending(cx))
238 return false;
240 // else...
242 if (!nsXPCException::NameAndFormatForNSResult(NS_ERROR_XPC_NATIVE_RETURNED_FAILURE, nullptr, &format) ||
243 !format) {
244 format = "";
247 JSAutoByteString memberNameBytes;
248 if (!memberName) {
249 memberName = JSID_IS_STRING(memberId)
250 ? memberNameBytes.encodeLatin1(cx, JSID_TO_STRING(memberId))
251 : "unknown";
253 if (nsXPCException::NameAndFormatForNSResult(rv, &name, nullptr)
254 && name) {
255 sz = JS_smprintf("%s 0x%x (%s) [%s.%s]",
256 format, rv, name, ifaceName, memberName);
257 } else {
258 sz = JS_smprintf("%s 0x%x [%s.%s]",
259 format, rv, ifaceName, memberName);
262 dom::Throw(cx, rv, sz);
264 if (sz)
265 JS_smprintf_free(sz);
267 return false;
270 bool
271 xpc_qsThrowGetterSetterFailed(JSContext* cx, nsresult rv, JSObject* obj,
272 jsid memberIdArg)
274 RootedId memberId(cx, memberIdArg);
275 const char* ifaceName;
276 GetMemberInfo(obj, memberId, &ifaceName);
277 return ThrowCallFailed(cx, rv, ifaceName, memberId, nullptr);
280 bool
281 xpc_qsThrowGetterSetterFailed(JSContext* cx, nsresult rv, JSObject* objArg,
282 const char* memberName)
284 RootedObject obj(cx, objArg);
285 JSString* str = JS_InternString(cx, memberName);
286 if (!str) {
287 return false;
289 return xpc_qsThrowGetterSetterFailed(cx, rv, obj,
290 INTERNED_STRING_TO_JSID(cx, str));
293 bool
294 xpc_qsThrowGetterSetterFailed(JSContext* cx, nsresult rv, JSObject* obj,
295 uint16_t memberIndex)
297 return xpc_qsThrowGetterSetterFailed(cx, rv, obj,
298 xpc_qsStringTable + memberIndex);
301 bool
302 xpc_qsThrowMethodFailed(JSContext* cx, nsresult rv, jsval* vp)
304 const char* ifaceName;
305 RootedId memberId(cx);
306 GetMethodInfo(cx, vp, &ifaceName, memberId.address());
307 return ThrowCallFailed(cx, rv, ifaceName, memberId, nullptr);
310 static void
311 ThrowBadArg(JSContext* cx, nsresult rv, const char* ifaceName,
312 jsid memberId, const char* memberName, unsigned paramnum)
314 /* Only one memberId or memberName should be given. */
315 MOZ_ASSERT(JSID_IS_VOID(memberId) != !memberName);
317 // From XPCThrower::ThrowBadParam.
318 char* sz;
319 const char* format;
321 if (!nsXPCException::NameAndFormatForNSResult(rv, nullptr, &format))
322 format = "";
324 JSAutoByteString memberNameBytes;
325 if (!memberName) {
326 memberName = JSID_IS_STRING(memberId)
327 ? memberNameBytes.encodeLatin1(cx, JSID_TO_STRING(memberId))
328 : "unknown";
330 sz = JS_smprintf("%s arg %u [%s.%s]",
331 format, (unsigned int) paramnum, ifaceName, memberName);
333 dom::Throw(cx, rv, sz);
335 if (sz)
336 JS_smprintf_free(sz);
339 void
340 xpc_qsThrowBadArg(JSContext* cx, nsresult rv, jsval* vp, unsigned paramnum)
342 const char* ifaceName;
343 RootedId memberId(cx);
344 GetMethodInfo(cx, vp, &ifaceName, memberId.address());
345 ThrowBadArg(cx, rv, ifaceName, memberId, nullptr, paramnum);
348 void
349 xpc_qsThrowBadArgWithCcx(XPCCallContext& ccx, nsresult rv, unsigned paramnum)
351 XPCThrower::ThrowBadParam(rv, paramnum, ccx);
354 void
355 xpc_qsThrowBadArgWithDetails(JSContext* cx, nsresult rv, unsigned paramnum,
356 const char* ifaceName, const char* memberName)
358 ThrowBadArg(cx, rv, ifaceName, JSID_VOID, memberName, paramnum);
361 void
362 xpc_qsThrowBadSetterValue(JSContext* cx, nsresult rv,
363 JSObject* obj, jsid propIdArg)
365 RootedId propId(cx, propIdArg);
366 const char* ifaceName;
367 GetMemberInfo(obj, propId, &ifaceName);
368 ThrowBadArg(cx, rv, ifaceName, propId, nullptr, 0);
371 void
372 xpc_qsThrowBadSetterValue(JSContext* cx, nsresult rv,
373 JSObject* objArg, const char* propName)
375 RootedObject obj(cx, objArg);
376 JSString* str = JS_InternString(cx, propName);
377 if (!str) {
378 return;
380 xpc_qsThrowBadSetterValue(cx, rv, obj, INTERNED_STRING_TO_JSID(cx, str));
383 void
384 xpc_qsThrowBadSetterValue(JSContext* cx, nsresult rv, JSObject* obj,
385 uint16_t name_index)
387 xpc_qsThrowBadSetterValue(cx, rv, obj, xpc_qsStringTable + name_index);
390 bool
391 xpc_qsGetterOnlyPropertyStub(JSContext* cx, HandleObject obj, HandleId id, bool strict,
392 MutableHandleValue vp)
394 return JS_ReportErrorFlagsAndNumber(cx,
395 JSREPORT_WARNING | JSREPORT_STRICT |
396 JSREPORT_STRICT_MODE_ERROR,
397 js_GetErrorMessage, nullptr,
398 JSMSG_GETTER_ONLY);
401 bool
402 xpc_qsGetterOnlyNativeStub(JSContext* cx, unsigned argc, jsval* vp)
404 return JS_ReportErrorFlagsAndNumber(cx,
405 JSREPORT_WARNING | JSREPORT_STRICT |
406 JSREPORT_STRICT_MODE_ERROR,
407 js_GetErrorMessage, nullptr,
408 JSMSG_GETTER_ONLY);
411 xpc_qsDOMString::xpc_qsDOMString(JSContext* cx, HandleValue v,
412 MutableHandleValue pval, bool notpassed,
413 StringificationBehavior nullBehavior,
414 StringificationBehavior undefinedBehavior)
416 typedef implementation_type::char_traits traits;
417 // From the T_DOMSTRING case in XPCConvert::JSData2Native.
418 JSString* s = InitOrStringify<traits>(cx, v,
419 pval, notpassed,
420 nullBehavior,
421 undefinedBehavior);
422 if (!s)
423 return;
425 nsAutoString* str = new(mBuf) implementation_type();
427 mValid = AssignJSString(cx, *str, s);
430 xpc_qsACString::xpc_qsACString(JSContext* cx, HandleValue v,
431 MutableHandleValue pval, bool notpassed,
432 StringificationBehavior nullBehavior,
433 StringificationBehavior undefinedBehavior)
435 typedef implementation_type::char_traits traits;
436 // From the T_CSTRING case in XPCConvert::JSData2Native.
437 JSString* s = InitOrStringify<traits>(cx, v,
438 pval, notpassed,
439 nullBehavior,
440 undefinedBehavior);
441 if (!s)
442 return;
444 size_t len = JS_GetStringEncodingLength(cx, s);
445 if (len == size_t(-1)) {
446 mValid = false;
447 return;
450 JSAutoByteString bytes(cx, s);
451 if (!bytes) {
452 mValid = false;
453 return;
456 new(mBuf) implementation_type(bytes.ptr(), len);
457 mValid = true;
460 xpc_qsAUTF8String::xpc_qsAUTF8String(JSContext* cx, HandleValue v, MutableHandleValue pval, bool notpassed)
462 typedef nsCharTraits<char16_t> traits;
463 // From the T_UTF8STRING case in XPCConvert::JSData2Native.
464 JSString* s = InitOrStringify<traits>(cx, v, pval, notpassed, eNull, eNull);
465 if (!s)
466 return;
468 nsAutoJSString str;
469 if (!str.init(cx, s)) {
470 mValid = false;
471 return;
474 new(mBuf) implementation_type(str);
475 mValid = true;
478 static nsresult
479 getNative(nsISupports* idobj,
480 HandleObject obj,
481 const nsIID& iid,
482 void** ppThis,
483 nsISupports** pThisRef,
484 jsval* vp)
486 nsresult rv = idobj->QueryInterface(iid, ppThis);
487 *pThisRef = static_cast<nsISupports*>(*ppThis);
488 if (NS_SUCCEEDED(rv))
489 *vp = OBJECT_TO_JSVAL(obj);
490 return rv;
493 static inline nsresult
494 getNativeFromWrapper(JSContext* cx,
495 XPCWrappedNative* wrapper,
496 const nsIID& iid,
497 void** ppThis,
498 nsISupports** pThisRef,
499 jsval* vp)
501 RootedObject obj(cx, wrapper->GetFlatJSObject());
502 return getNative(wrapper->GetIdentityObject(), obj, iid, ppThis, pThisRef,
503 vp);
507 nsresult
508 getWrapper(JSContext* cx,
509 JSObject* obj,
510 XPCWrappedNative** wrapper,
511 JSObject** cur,
512 XPCWrappedNativeTearOff** tearoff)
514 // We can have at most three layers in need of unwrapping here:
515 // * A (possible) security wrapper
516 // * A (possible) Xray waiver
517 // * A (possible) outer window
519 // If we pass stopAtOuter == false, we can handle all three with one call
520 // to js::CheckedUnwrap.
521 if (js::IsWrapper(obj)) {
522 JSObject* inner = js::CheckedUnwrap(obj, /* stopAtOuter = */ false);
524 // The safe unwrap might have failed if we encountered an object that
525 // we're not allowed to unwrap. If it didn't fail though, we should be
526 // done with wrappers.
527 if (!inner)
528 return NS_ERROR_XPC_SECURITY_MANAGER_VETO;
529 MOZ_ASSERT(!js::IsWrapper(inner));
531 obj = inner;
534 // Start with sane values.
535 *wrapper = nullptr;
536 *cur = nullptr;
537 *tearoff = nullptr;
539 if (dom::IsDOMObject(obj)) {
540 *cur = obj;
542 return NS_OK;
545 // Handle tearoffs.
547 // If |obj| is of the tearoff class, that means we're dealing with a JS
548 // object reflection of a particular interface (ie, |foo.nsIBar|). These
549 // JS objects are parented to their wrapper, so we snag the tearoff object
550 // along the way (if desired), and then set |obj| to its parent.
551 const js::Class* clasp = js::GetObjectClass(obj);
552 if (clasp == &XPC_WN_Tearoff_JSClass) {
553 *tearoff = (XPCWrappedNativeTearOff*) js::GetObjectPrivate(obj);
554 obj = js::GetObjectParent(obj);
557 // If we've got a WN, store things the way callers expect. Otherwise, leave
558 // things null and return.
559 if (IS_WN_CLASS(clasp))
560 *wrapper = XPCWrappedNative::Get(obj);
562 return NS_OK;
565 nsresult
566 castNative(JSContext* cx,
567 XPCWrappedNative* wrapper,
568 JSObject* curArg,
569 XPCWrappedNativeTearOff* tearoff,
570 const nsIID& iid,
571 void** ppThis,
572 nsISupports** pThisRef,
573 MutableHandleValue vp)
575 RootedObject cur(cx, curArg);
576 if (wrapper) {
577 nsresult rv = getNativeFromWrapper(cx,wrapper, iid, ppThis, pThisRef,
578 vp.address());
580 if (rv != NS_ERROR_NO_INTERFACE)
581 return rv;
582 } else if (cur) {
583 nsISupports* native;
584 if (!(native = mozilla::dom::UnwrapDOMObjectToISupports(cur))) {
585 *pThisRef = nullptr;
586 return NS_ERROR_ILLEGAL_VALUE;
589 if (NS_SUCCEEDED(getNative(native, cur, iid, ppThis, pThisRef, vp.address()))) {
590 return NS_OK;
594 *pThisRef = nullptr;
595 return NS_ERROR_XPC_BAD_OP_ON_WN_PROTO;
598 nsISupports*
599 castNativeFromWrapper(JSContext* cx,
600 JSObject* obj,
601 uint32_t interfaceBit,
602 uint32_t protoID,
603 int32_t protoDepth,
604 nsISupports** pRef,
605 MutableHandleValue pVal,
606 nsresult* rv)
608 XPCWrappedNative* wrapper;
609 XPCWrappedNativeTearOff* tearoff;
610 JSObject* cur;
612 if (IS_WN_REFLECTOR(obj)) {
613 cur = obj;
614 wrapper = XPCWrappedNative::Get(obj);
615 tearoff = nullptr;
616 } else {
617 *rv = getWrapper(cx, obj, &wrapper, &cur, &tearoff);
618 if (NS_FAILED(*rv))
619 return nullptr;
622 nsISupports* native;
623 if (wrapper) {
624 native = wrapper->GetIdentityObject();
625 cur = wrapper->GetFlatJSObject();
626 if (!native || !HasBitInInterfacesBitmap(cur, interfaceBit)) {
627 native = nullptr;
629 } else if (cur && protoDepth >= 0) {
630 const mozilla::dom::DOMJSClass* domClass =
631 mozilla::dom::GetDOMClass(cur);
632 native = mozilla::dom::UnwrapDOMObject<nsISupports>(cur);
633 if (native &&
634 (uint32_t)domClass->mInterfaceChain[protoDepth] != protoID) {
635 native = nullptr;
637 } else {
638 native = nullptr;
641 if (native) {
642 *pRef = nullptr;
643 pVal.setObjectOrNull(cur);
644 *rv = NS_OK;
645 } else {
646 *rv = NS_ERROR_XPC_BAD_CONVERT_JS;
649 return native;
652 nsresult
653 xpc_qsUnwrapArgImpl(JSContext* cx,
654 HandleValue v,
655 const nsIID& iid,
656 void** ppArg,
657 nsISupports** ppArgRef,
658 MutableHandleValue vp)
660 nsresult rv;
661 RootedObject src(cx, xpc_qsUnwrapObj(v, ppArgRef, &rv));
662 if (!src) {
663 *ppArg = nullptr;
665 return rv;
668 XPCWrappedNative* wrapper;
669 XPCWrappedNativeTearOff* tearoff;
670 JSObject* obj2;
671 rv = getWrapper(cx, src, &wrapper, &obj2, &tearoff);
672 NS_ENSURE_SUCCESS(rv, rv);
674 if (wrapper || obj2) {
675 if (NS_FAILED(castNative(cx, wrapper, obj2, tearoff, iid, ppArg,
676 ppArgRef, vp)))
677 return NS_ERROR_XPC_BAD_CONVERT_JS;
678 return NS_OK;
680 // else...
681 // Slow path.
683 // Try to unwrap a slim wrapper.
684 nsISupports* iface;
685 if (XPCConvert::GetISupportsFromJSObject(src, &iface)) {
686 if (!iface || NS_FAILED(iface->QueryInterface(iid, ppArg))) {
687 *ppArgRef = nullptr;
688 return NS_ERROR_XPC_BAD_CONVERT_JS;
691 *ppArgRef = static_cast<nsISupports*>(*ppArg);
692 return NS_OK;
695 // Create the ccx needed for quick stubs.
696 XPCCallContext ccx(JS_CALLER, cx);
697 if (!ccx.IsValid()) {
698 *ppArgRef = nullptr;
699 return NS_ERROR_XPC_BAD_CONVERT_JS;
702 nsRefPtr<nsXPCWrappedJS> wrappedJS;
703 rv = nsXPCWrappedJS::GetNewOrUsed(src, iid, getter_AddRefs(wrappedJS));
704 if (NS_FAILED(rv) || !wrappedJS) {
705 *ppArgRef = nullptr;
706 return rv;
709 // We need to go through the QueryInterface logic to make this return
710 // the right thing for the various 'special' interfaces; e.g.
711 // nsIPropertyBag. We must use AggregatedQueryInterface in cases where
712 // there is an outer to avoid nasty recursion.
713 rv = wrappedJS->QueryInterface(iid, ppArg);
714 if (NS_SUCCEEDED(rv)) {
715 *ppArgRef = static_cast<nsISupports*>(*ppArg);
716 vp.setObjectOrNull(wrappedJS->GetJSObject());
718 return rv;
721 bool
722 xpc_qsJsvalToCharStr(JSContext* cx, HandleValue v, JSAutoByteString* bytes)
724 MOZ_ASSERT(!bytes->ptr());
726 if (v.isNullOrUndefined())
727 return true;
729 JSString* str = ToString(cx, v);
730 if (!str)
731 return false;
732 return !!bytes->encodeLatin1(cx, str);
735 namespace xpc {
737 bool
738 NonVoidStringToJsval(JSContext* cx, nsAString& str, MutableHandleValue rval)
740 nsStringBuffer* sharedBuffer;
741 if (!XPCStringConvert::ReadableToJSVal(cx, str, &sharedBuffer, rval))
742 return false;
744 if (sharedBuffer) {
745 // The string was shared but ReadableToJSVal didn't addref it.
746 // Move the ownership from str to jsstr.
747 str.ForgetSharedBuffer();
749 return true;
752 } // namespace xpc
754 bool
755 xpc_qsXPCOMObjectToJsval(JSContext* cx, qsObjectHelper& aHelper,
756 const nsIID* iid, XPCNativeInterface** iface,
757 MutableHandleValue rval)
759 NS_PRECONDITION(iface, "Who did that and why?");
761 // From the T_INTERFACE case in XPCConvert::NativeData2JS.
762 // This is one of the slowest things quick stubs do.
764 nsresult rv;
765 if (!XPCConvert::NativeInterface2JSObject(rval, nullptr,
766 aHelper, iid, iface,
767 true, &rv)) {
768 // I can't tell if NativeInterface2JSObject throws JS exceptions
769 // or not. This is a sloppy stab at the right semantics; the
770 // method really ought to be fixed to behave consistently.
771 if (!JS_IsExceptionPending(cx))
772 xpc_qsThrow(cx, NS_FAILED(rv) ? rv : NS_ERROR_UNEXPECTED);
773 return false;
776 #ifdef DEBUG
777 JSObject* jsobj = rval.toObjectOrNull();
778 if (jsobj && !js::GetObjectParent(jsobj))
779 MOZ_ASSERT(js::GetObjectClass(jsobj)->flags & JSCLASS_IS_GLOBAL,
780 "Why did we recreate this wrapper?");
781 #endif
783 return true;
786 bool
787 xpc_qsVariantToJsval(JSContext* aCx,
788 nsIVariant* p,
789 MutableHandleValue rval)
791 // From the T_INTERFACE case in XPCConvert::NativeData2JS.
792 // Error handling is in XPCWrappedNative::CallMethod.
793 if (p) {
794 nsresult rv;
795 bool ok = XPCVariant::VariantDataToJS(p, &rv, rval);
796 if (!ok)
797 xpc_qsThrow(aCx, rv);
798 return ok;
800 rval.setNull();
801 return true;
804 #ifdef DEBUG
805 void
806 xpc_qsAssertContextOK(JSContext* cx)
808 XPCJSContextStack* stack = XPCJSRuntime::Get()->GetJSContextStack();
810 JSContext* topJSContext = stack->Peek();
812 // This is what we're actually trying to assert here.
813 MOZ_ASSERT(cx == topJSContext, "wrong context on XPCJSContextStack!");
815 #endif