js/xpconnect/src/XPCQuickStubs.cpp

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

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

mercurial