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