tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

XPCJSID.cpp (22029B)


      1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
      2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
      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/. */
      6 
      7 /* An xpcom implementation of the JavaScript nsIID and nsCID objects. */
      8 
      9 #include "xpcprivate.h"
     10 #include "mozilla/dom/BindingUtils.h"
     11 #include "js/Object.h"              // JS::GetClass, JS::GetReservedSlot
     12 #include "js/PropertyAndElement.h"  // JS_DefineFunction, JS_DefineFunctionById, JS_DefineProperty, JS_DefinePropertyById
     13 #include "js/Symbol.h"
     14 #include "nsContentUtils.h"
     15 
     16 using namespace mozilla;
     17 using namespace mozilla::dom;
     18 using namespace JS;
     19 
     20 namespace xpc {
     21 
     22 /******************************************************************************
     23 * # Generic IDs #
     24 *
     25 * Generic IDs are the type of JS object created by most code which passes nsID
     26 * objects to JavaScript code. They provide no special methods, and only store
     27 * the raw nsID value.
     28 *
     29 * The nsID value is stored in 4 reserved slots, with 32 bits of the 128-bit
     30 * value stored in each slot. Getter code extracts this data, and combines them
     31 * back into the nsID value.
     32 */
     33 static bool ID_Equals(JSContext* aCx, unsigned aArgc, Value* aVp);
     34 static bool ID_GetNumber(JSContext* aCx, unsigned aArgc, Value* aVp);
     35 
     36 // Generic ID objects contain 4 reserved slots, each containing a uint32_t with
     37 // 1/4 of the representation of the nsID value. This allows us to avoid an extra
     38 // allocation for the nsID object, and eliminates the need for a finalizer.
     39 enum { kID_Slot0, kID_Slot1, kID_Slot2, kID_Slot3, kID_SlotCount };
     40 static const JSClass sID_Class = {
     41    "nsJSID", JSCLASS_HAS_RESERVED_SLOTS(kID_SlotCount), JS_NULL_CLASS_OPS};
     42 
     43 /******************************************************************************
     44 * # Interface IDs #
     45 *
     46 * In addition to the properties exposed by Generic ID objects, IID supports
     47 * 'instanceof', exposes constant properties defined on the class, and exposes
     48 * the interface name as the 'name' and 'toString()' values.
     49 */
     50 static bool IID_HasInstance(JSContext* aCx, unsigned aArgc, Value* aVp);
     51 static bool IID_GetName(JSContext* aCx, unsigned aArgc, Value* aVp);
     52 
     53 static bool IID_NewEnumerate(JSContext* cx, HandleObject obj,
     54                             MutableHandleIdVector properties,
     55                             bool enumerableOnly);
     56 static bool IID_Resolve(JSContext* cx, HandleObject obj, HandleId id,
     57                        bool* resolvedp);
     58 static bool IID_MayResolve(const JSAtomState& names, jsid id,
     59                           JSObject* maybeObj);
     60 
     61 static const JSClassOps sIID_ClassOps = {
     62    nullptr,           // addProperty
     63    nullptr,           // delProperty
     64    nullptr,           // enumerate
     65    IID_NewEnumerate,  // newEnumerate
     66    IID_Resolve,       // resolve
     67    IID_MayResolve,    // mayResolve
     68    nullptr,           // finalize
     69    nullptr,           // call
     70    nullptr,           // construct
     71    nullptr,           // trace
     72 };
     73 
     74 // Interface ID objects use a single reserved slot containing a pointer to the
     75 // nsXPTInterfaceInfo object for the interface in question.
     76 enum { kIID_InfoSlot, kIID_SlotCount };
     77 static const JSClass sIID_Class = {
     78    "nsJSIID", JSCLASS_HAS_RESERVED_SLOTS(kIID_SlotCount), &sIID_ClassOps};
     79 
     80 /******************************************************************************
     81 * # Contract IDs #
     82 *
     83 * In addition to the properties exposed by Generic ID objects, Contract IDs
     84 * expose 'getService' and 'createInstance' methods, and expose the contractID
     85 * string as '.name' and '.toString()'.
     86 */
     87 static bool CID_CreateInstance(JSContext* aCx, unsigned aArgc, Value* aVp);
     88 static bool CID_GetService(JSContext* aCx, unsigned aArgc, Value* aVp);
     89 static bool CID_GetName(JSContext* aCx, unsigned aArgc, Value* aVp);
     90 
     91 // ContractID objects use a single reserved slot, containing the ContractID. The
     92 // nsCID value for this object is looked up when the object is being unwrapped.
     93 enum { kCID_ContractSlot, kCID_SlotCount };
     94 static const JSClass sCID_Class = {
     95    "nsJSCID", JSCLASS_HAS_RESERVED_SLOTS(kCID_SlotCount), JS_NULL_CLASS_OPS};
     96 
     97 /**
     98 * Ensure that the nsID prototype objects have been created for the current
     99 * global, and extract the prototype values.
    100 */
    101 static JSObject* GetIDPrototype(JSContext* aCx, const JSClass* aClass) {
    102  XPCWrappedNativeScope* scope = ObjectScope(CurrentGlobalOrNull(aCx));
    103  if (NS_WARN_IF(!scope)) {
    104    return nullptr;
    105  }
    106 
    107  // Create prototype objects for the JSID objects if they haven't been
    108  // created for this scope yet.
    109  if (!scope->mIDProto) {
    110    MOZ_ASSERT(!scope->mIIDProto && !scope->mCIDProto);
    111 
    112    RootedObject idProto(aCx, JS_NewPlainObject(aCx));
    113    RootedObject iidProto(aCx,
    114                          JS_NewObjectWithGivenProto(aCx, nullptr, idProto));
    115    RootedObject cidProto(aCx,
    116                          JS_NewObjectWithGivenProto(aCx, nullptr, idProto));
    117    RootedId hasInstance(aCx,
    118                         GetWellKnownSymbolKey(aCx, SymbolCode::hasInstance));
    119 
    120    const uint32_t kFlags =
    121        JSPROP_READONLY | JSPROP_ENUMERATE | JSPROP_PERMANENT;
    122    const uint32_t kNoEnum = JSPROP_READONLY | JSPROP_PERMANENT;
    123 
    124    bool ok =
    125        idProto && iidProto && cidProto &&
    126        // Methods and properties on all ID Objects:
    127        JS_DefineFunction(aCx, idProto, "equals", ID_Equals, 1, kFlags) &&
    128        JS_DefineProperty(aCx, idProto, "number", ID_GetNumber, nullptr,
    129                          kFlags) &&
    130 
    131        // Methods for IfaceID objects, which also inherit ID properties:
    132        JS_DefineFunctionById(aCx, iidProto, hasInstance, IID_HasInstance, 1,
    133                              kNoEnum) &&
    134        JS_DefineProperty(aCx, iidProto, "name", IID_GetName, nullptr,
    135                          kFlags) &&
    136 
    137        // Methods for ContractID objects, which also inherit ID properties:
    138        JS_DefineFunction(aCx, cidProto, "createInstance", CID_CreateInstance,
    139                          1, kFlags) &&
    140        JS_DefineFunction(aCx, cidProto, "getService", CID_GetService, 1,
    141                          kFlags) &&
    142        JS_DefineProperty(aCx, cidProto, "name", CID_GetName, nullptr,
    143                          kFlags) &&
    144 
    145        // ToString returns '.number' on generic IDs, while returning
    146        // '.name' on other ID types.
    147        JS_DefineFunction(aCx, idProto, "toString", ID_GetNumber, 0, kFlags) &&
    148        JS_DefineFunction(aCx, iidProto, "toString", IID_GetName, 0, kFlags) &&
    149        JS_DefineFunction(aCx, cidProto, "toString", CID_GetName, 0, kFlags);
    150    if (!ok) {
    151      return nullptr;
    152    }
    153 
    154    scope->mIDProto = idProto;
    155    scope->mIIDProto = iidProto;
    156    scope->mCIDProto = cidProto;
    157  }
    158 
    159  if (aClass == &sID_Class) {
    160    return scope->mIDProto;
    161  } else if (aClass == &sIID_Class) {
    162    return scope->mIIDProto;
    163  } else if (aClass == &sCID_Class) {
    164    return scope->mCIDProto;
    165  }
    166 
    167  MOZ_CRASH("Unrecognized ID Object Class");
    168 }
    169 
    170 // Unwrap the given value to an object with the correct class, or nullptr.
    171 static JSObject* GetIDObject(HandleValue aVal, const JSClass* aClass) {
    172  if (aVal.isObject()) {
    173    // We care only about IID/CID objects here, so CheckedUnwrapStatic is fine.
    174    JSObject* obj = js::CheckedUnwrapStatic(&aVal.toObject());
    175    if (obj && JS::GetClass(obj) == aClass) {
    176      return obj;
    177    }
    178  }
    179  return nullptr;
    180 }
    181 
    182 static const nsXPTInterfaceInfo* GetInterfaceInfo(JSObject* obj) {
    183  MOZ_ASSERT(JS::GetClass(obj) == &sIID_Class);
    184  return static_cast<const nsXPTInterfaceInfo*>(
    185      JS::GetReservedSlot(obj, kIID_InfoSlot).toPrivate());
    186 }
    187 
    188 /**
    189 * Unwrap an nsID object from a JSValue.
    190 *
    191 * For Generic ID objects, this function will extract the nsID from reserved
    192 * slots. For IfaceID objects, it will be extracted from the nsXPTInterfaceInfo,
    193 * and for ContractID objects, the ContractID's corresponding CID will be looked
    194 * up.
    195 */
    196 Maybe<nsID> JSValue2ID(JSContext* aCx, HandleValue aVal) {
    197  if (!aVal.isObject()) {
    198    return Nothing();
    199  }
    200 
    201  // We only care about ID objects here, so CheckedUnwrapStatic is fine.
    202  RootedObject obj(aCx, js::CheckedUnwrapStatic(&aVal.toObject()));
    203  if (!obj) {
    204    return Nothing();
    205  }
    206 
    207  mozilla::Maybe<nsID> id;
    208  if (JS::GetClass(obj) == &sID_Class) {
    209    // Extract the raw bytes of the nsID from reserved slots.
    210    uint32_t rawid[] = {JS::GetReservedSlot(obj, kID_Slot0).toPrivateUint32(),
    211                        JS::GetReservedSlot(obj, kID_Slot1).toPrivateUint32(),
    212                        JS::GetReservedSlot(obj, kID_Slot2).toPrivateUint32(),
    213                        JS::GetReservedSlot(obj, kID_Slot3).toPrivateUint32()};
    214 
    215    // Construct a nsID inside the Maybe, and copy the rawid into it.
    216    id.emplace();
    217    memcpy(id.ptr(), &rawid, sizeof(nsID));
    218  } else if (JS::GetClass(obj) == &sIID_Class) {
    219    // IfaceID objects store a nsXPTInterfaceInfo* pointer.
    220    const nsXPTInterfaceInfo* info = GetInterfaceInfo(obj);
    221    id.emplace(info->IID());
    222  } else if (JS::GetClass(obj) == &sCID_Class) {
    223    // ContractID objects store a ContractID string.
    224    JS::UniqueChars contractId = JS_EncodeStringToLatin1(
    225        aCx, JS::GetReservedSlot(obj, kCID_ContractSlot).toString());
    226 
    227    // NOTE(nika): If we directly access the nsComponentManager, we can do
    228    // this with a more-basic pointer lookup:
    229    //     nsFactoryEntry* entry = nsComponentManagerImpl::gComponentManager->
    230    //         GetFactoryEntry(contractId.ptr(), contractId.length());
    231    //     if (entry) id.emplace(entry->mCIDEntry->cid);
    232 
    233    nsCOMPtr<nsIComponentRegistrar> registrar;
    234    nsresult rv = NS_GetComponentRegistrar(getter_AddRefs(registrar));
    235    if (NS_FAILED(rv) || !registrar) {
    236      return Nothing();
    237    }
    238 
    239    nsCID* cid = nullptr;
    240    if (NS_SUCCEEDED(registrar->ContractIDToCID(contractId.get(), &cid))) {
    241      id.emplace(*cid);
    242      free(cid);
    243    }
    244  }
    245  return id;
    246 }
    247 
    248 /**
    249 * Public ID Object Constructor Methods
    250 */
    251 static JSObject* NewIDObjectHelper(JSContext* aCx, const JSClass* aClass) {
    252  RootedObject proto(aCx, GetIDPrototype(aCx, aClass));
    253  if (proto) {
    254    return JS_NewObjectWithGivenProto(aCx, aClass, proto);
    255  }
    256  return nullptr;
    257 }
    258 
    259 bool ID2JSValue(JSContext* aCx, const nsID& aId, MutableHandleValue aVal) {
    260  RootedObject obj(aCx, NewIDObjectHelper(aCx, &sID_Class));
    261  if (!obj) {
    262    return false;
    263  }
    264 
    265  // Get the data in nsID as 4 uint32_ts, and store them in slots.
    266  uint32_t rawid[4];
    267  memcpy(&rawid, &aId, sizeof(nsID));
    268  static_assert(sizeof(nsID) == sizeof(rawid), "Wrong size of nsID");
    269  JS::SetReservedSlot(obj, kID_Slot0, PrivateUint32Value(rawid[0]));
    270  JS::SetReservedSlot(obj, kID_Slot1, PrivateUint32Value(rawid[1]));
    271  JS::SetReservedSlot(obj, kID_Slot2, PrivateUint32Value(rawid[2]));
    272  JS::SetReservedSlot(obj, kID_Slot3, PrivateUint32Value(rawid[3]));
    273 
    274  aVal.setObject(*obj);
    275  return true;
    276 }
    277 
    278 bool IfaceID2JSValue(JSContext* aCx, const nsXPTInterfaceInfo& aInfo,
    279                     MutableHandleValue aVal) {
    280  RootedObject obj(aCx, NewIDObjectHelper(aCx, &sIID_Class));
    281  if (!obj) {
    282    return false;
    283  }
    284 
    285  // The InterfaceInfo is stored in a reserved slot.
    286  JS::SetReservedSlot(obj, kIID_InfoSlot, PrivateValue((void*)&aInfo));
    287  aVal.setObject(*obj);
    288  return true;
    289 }
    290 
    291 bool ContractID2JSValue(JSContext* aCx, JSString* aContract,
    292                        MutableHandleValue aVal) {
    293  RootedString jsContract(aCx, aContract);
    294 
    295  {
    296    // It is perfectly safe to have a ContractID object with an invalid
    297    // ContractID, but is usually a bug.
    298    nsCOMPtr<nsIComponentRegistrar> registrar;
    299    NS_GetComponentRegistrar(getter_AddRefs(registrar));
    300    if (!registrar) {
    301      return false;
    302    }
    303 
    304    bool registered = false;
    305    JS::UniqueChars contract = JS_EncodeStringToLatin1(aCx, jsContract);
    306    registrar->IsContractIDRegistered(contract.get(), &registered);
    307    if (!registered) {
    308      return false;
    309    }
    310  }
    311 
    312  RootedObject obj(aCx, NewIDObjectHelper(aCx, &sCID_Class));
    313  if (!obj) {
    314    return false;
    315  }
    316 
    317  // The Contract is stored in a reserved slot.
    318  JS::SetReservedSlot(obj, kCID_ContractSlot, StringValue(jsContract));
    319  aVal.setObject(*obj);
    320  return true;
    321 }
    322 
    323 /******************************************************************************
    324 * # Method & Property Getter Implementations #
    325 */
    326 
    327 // NOTE: This method is used both for 'get ID.prototype.number' and
    328 // 'ID.prototype.toString'.
    329 static bool ID_GetNumber(JSContext* aCx, unsigned aArgc, Value* aVp) {
    330  CallArgs args = CallArgsFromVp(aArgc, aVp);
    331 
    332  Maybe<nsID> id = JSValue2ID(aCx, args.thisv());
    333  if (!id) {
    334    return Throw(aCx, NS_ERROR_XPC_BAD_CONVERT_JS);
    335  }
    336 
    337  char buf[NSID_LENGTH];
    338  id->ToProvidedString(buf);
    339  JSString* jsnum = JS_NewStringCopyZ(aCx, buf);
    340  if (!jsnum) {
    341    return Throw(aCx, NS_ERROR_OUT_OF_MEMORY);
    342  }
    343 
    344  args.rval().setString(jsnum);
    345  return true;
    346 }
    347 
    348 static bool ID_Equals(JSContext* aCx, unsigned aArgc, Value* aVp) {
    349  CallArgs args = CallArgsFromVp(aArgc, aVp);
    350  if (!args.requireAtLeast(aCx, "nsID.equals", 1)) {
    351    return false;
    352  }
    353 
    354  Maybe<nsID> id = JSValue2ID(aCx, args.thisv());
    355  Maybe<nsID> id2 = JSValue2ID(aCx, args[0]);
    356  if (!id || !id2) {
    357    return Throw(aCx, NS_ERROR_XPC_BAD_CONVERT_JS);
    358  }
    359 
    360  args.rval().setBoolean(id->Equals(*id2));
    361  return true;
    362 }
    363 
    364 /*
    365 * HasInstance hooks need to find an appropriate reflector in order to function
    366 * properly. There are two complexities that we need to handle:
    367 *
    368 * 1 - Cross-compartment wrappers. Chrome uses over 100 compartments, all with
    369 *     system principal. The success of an instanceof check should not depend
    370 *     on which compartment an object comes from. At the same time, we want to
    371 *     make sure we don't unwrap important security wrappers.
    372 *     CheckedUnwrap does the right thing here.
    373 *
    374 * 2 - Prototype chains. Suppose someone creates a vanilla JS object |a| and
    375 *     sets its __proto__ to some WN |b|. If |b instanceof nsIFoo| returns true,
    376 *     one would expect |a instanceof nsIFoo| to return true as well, since
    377 *     instanceof is transitive up the prototype chain in ECMAScript. Moreover,
    378 *     there's chrome code that relies on this.
    379 *
    380 * This static method handles both complexities, returning either an XPCWN, a
    381 * DOM object, or null. The object may well be cross-compartment from |cx|.
    382 */
    383 static nsresult FindObjectForHasInstance(JSContext* cx, HandleObject objArg,
    384                                         MutableHandleObject target) {
    385  RootedObject obj(cx, objArg), proto(cx);
    386  while (true) {
    387    // Try the object, or the wrappee if allowed.  We want CheckedUnwrapDynamic
    388    // here, because we might in fact be looking for a Window.  "cx" represents
    389    // our current global.
    390    JSObject* o =
    391        js::IsWrapper(obj) ? js::CheckedUnwrapDynamic(obj, cx, false) : obj;
    392    if (o && (IsWrappedNativeReflector(o) || IsDOMObject(o))) {
    393      target.set(o);
    394      return NS_OK;
    395    }
    396 
    397    // Walk the prototype chain from the perspective of the callee (i.e.
    398    // respecting Xrays if they exist).
    399    if (!js::GetObjectProto(cx, obj, &proto)) {
    400      return NS_ERROR_FAILURE;
    401    }
    402    if (!proto) {
    403      target.set(nullptr);
    404      return NS_OK;
    405    }
    406    obj = proto;
    407  }
    408 }
    409 
    410 nsresult HasInstance(JSContext* cx, HandleObject objArg, const nsID* iid,
    411                     bool* bp) {
    412  *bp = false;
    413 
    414  RootedObject obj(cx);
    415  nsresult rv = FindObjectForHasInstance(cx, objArg, &obj);
    416  if (NS_WARN_IF(NS_FAILED(rv))) {
    417    return rv;
    418  }
    419 
    420  if (!obj) {
    421    return NS_OK;
    422  }
    423 
    424  // Need to unwrap Window correctly here, so use ReflectorToISupportsDynamic.
    425  nsCOMPtr<nsISupports> identity = ReflectorToISupportsDynamic(obj, cx);
    426  if (!identity) {
    427    return NS_OK;
    428  }
    429 
    430  nsCOMPtr<nsISupports> supp;
    431  identity->QueryInterface(*iid, getter_AddRefs(supp));
    432  *bp = supp;
    433 
    434  // Our old HasInstance implementation operated by invoking FindTearOff on
    435  // XPCWrappedNatives, and various bits of chrome JS came to depend on
    436  // |instanceof| doing an implicit QI if it succeeds. Do a drive-by QI to
    437  // preserve that behavior. This is just a compatibility hack, so we don't
    438  // really care if it fails.
    439  if (IsWrappedNativeReflector(obj)) {
    440    (void)XPCWrappedNative::Get(obj)->FindTearOff(cx, *iid);
    441  }
    442 
    443  return NS_OK;
    444 }
    445 
    446 static bool IID_HasInstance(JSContext* aCx, unsigned aArgc, Value* aVp) {
    447  CallArgs args = CallArgsFromVp(aArgc, aVp);
    448  if (!args.requireAtLeast(aCx, "nsIID[Symbol.hasInstance]", 1)) {
    449    return false;
    450  }
    451 
    452  Maybe<nsID> id = JSValue2ID(aCx, args.thisv());
    453  if (!id) {
    454    return Throw(aCx, NS_ERROR_XPC_BAD_CONVERT_JS);
    455  }
    456 
    457  bool hasInstance = false;
    458  if (args[0].isObject()) {
    459    RootedObject target(aCx, &args[0].toObject());
    460    nsresult rv = HasInstance(aCx, target, id.ptr(), &hasInstance);
    461    if (NS_FAILED(rv)) {
    462      return Throw(aCx, rv);
    463    }
    464  }
    465  args.rval().setBoolean(hasInstance);
    466  return true;
    467 }
    468 
    469 // NOTE: This method is used both for 'get IID.prototype.name' and
    470 // 'IID.prototype.toString'.
    471 static bool IID_GetName(JSContext* aCx, unsigned aArgc, Value* aVp) {
    472  CallArgs args = CallArgsFromVp(aArgc, aVp);
    473 
    474  RootedObject obj(aCx, GetIDObject(args.thisv(), &sIID_Class));
    475  if (!obj) {
    476    return Throw(aCx, NS_ERROR_XPC_BAD_CONVERT_JS);
    477  }
    478 
    479  const nsXPTInterfaceInfo* info = GetInterfaceInfo(obj);
    480 
    481  // Name property is the name of the interface this nsIID was created from.
    482  JSString* name = JS_NewStringCopyZ(aCx, info->Name());
    483  if (!name) {
    484    return Throw(aCx, NS_ERROR_OUT_OF_MEMORY);
    485  }
    486 
    487  args.rval().setString(name);
    488  return true;
    489 }
    490 
    491 static bool IID_NewEnumerate(JSContext* cx, HandleObject obj,
    492                             MutableHandleIdVector properties,
    493                             bool enumerableOnly) {
    494  const nsXPTInterfaceInfo* info = GetInterfaceInfo(obj);
    495 
    496  if (!properties.reserve(info->ConstantCount())) {
    497    JS_ReportOutOfMemory(cx);
    498    return false;
    499  }
    500 
    501  RootedId id(cx);
    502  RootedString name(cx);
    503  for (uint16_t i = 0; i < info->ConstantCount(); ++i) {
    504    name = JS_AtomizeString(cx, info->Constant(i).Name());
    505    if (!name || !JS_StringToId(cx, name, &id)) {
    506      return false;
    507    }
    508    properties.infallibleAppend(id);
    509  }
    510 
    511  return true;
    512 }
    513 
    514 static bool IID_Resolve(JSContext* cx, HandleObject obj, HandleId id,
    515                        bool* resolvedp) {
    516  *resolvedp = false;
    517  if (!id.isString()) {
    518    return true;
    519  }
    520 
    521  JSLinearString* name = id.toLinearString();
    522  const nsXPTInterfaceInfo* info = GetInterfaceInfo(obj);
    523  for (uint16_t i = 0; i < info->ConstantCount(); ++i) {
    524    if (JS_LinearStringEqualsAscii(name, info->Constant(i).Name())) {
    525      *resolvedp = true;
    526 
    527      RootedValue constant(cx, info->Constant(i).JSValue());
    528      return JS_DefinePropertyById(
    529          cx, obj, id, constant,
    530          JSPROP_READONLY | JSPROP_ENUMERATE | JSPROP_PERMANENT);
    531    }
    532  }
    533  return true;
    534 }
    535 
    536 static bool IID_MayResolve(const JSAtomState& names, jsid id,
    537                           JSObject* maybeObj) {
    538  if (!id.isString()) {
    539    return false;
    540  }
    541 
    542  if (!maybeObj) {
    543    // Each interface object has its own set of constants, so if we don't know
    544    // the object, assume any string property may be resolved.
    545    return true;
    546  }
    547 
    548  JSLinearString* name = id.toLinearString();
    549  const nsXPTInterfaceInfo* info = GetInterfaceInfo(maybeObj);
    550  for (uint16_t i = 0; i < info->ConstantCount(); ++i) {
    551    if (JS_LinearStringEqualsAscii(name, info->Constant(i).Name())) {
    552      return true;
    553    }
    554  }
    555  return false;
    556 }
    557 
    558 // Common code for CID_CreateInstance and CID_GetService
    559 static bool CIGSHelper(JSContext* aCx, unsigned aArgc, Value* aVp,
    560                       bool aGetService) {
    561  CallArgs args = CallArgsFromVp(aArgc, aVp);
    562 
    563  // Extract the ContractID string from our reserved slot. Don't use
    564  // JSValue2ID as this method should only be defined on Contract ID objects,
    565  // and it allows us to avoid a duplicate hashtable lookup.
    566  RootedObject obj(aCx, GetIDObject(args.thisv(), &sCID_Class));
    567  if (!obj) {
    568    return Throw(aCx, NS_ERROR_XPC_BAD_CONVERT_JS);
    569  }
    570  JS::UniqueChars contractID = JS_EncodeStringToLatin1(
    571      aCx, JS::GetReservedSlot(obj, kCID_ContractSlot).toString());
    572 
    573  // Extract the IID from the first argument, if passed. Default: nsISupports.
    574  Maybe<nsIID> iid = args.length() >= 1 ? JSValue2ID(aCx, args[0])
    575                                        : Some(NS_GET_IID(nsISupports));
    576  if (!iid) {
    577    return Throw(aCx, NS_ERROR_XPC_BAD_CONVERT_JS);
    578  }
    579 
    580  // Invoke CreateInstance or GetService with our ContractID.
    581  nsresult rv;
    582  nsCOMPtr<nsISupports> result;
    583  if (aGetService) {
    584    rv = CallGetService(contractID.get(), *iid, getter_AddRefs(result));
    585    if (NS_FAILED(rv) || !result) {
    586      return Throw(aCx, NS_ERROR_XPC_GS_RETURNED_FAILURE);
    587    }
    588  } else {
    589    rv = CallCreateInstance(contractID.get(), *iid, getter_AddRefs(result));
    590    if (NS_FAILED(rv) || !result) {
    591      return Throw(aCx, NS_ERROR_XPC_CI_RETURNED_FAILURE);
    592    }
    593  }
    594 
    595  // Wrap the created object and return it.
    596  rv = nsContentUtils::WrapNative(aCx, result, iid.ptr(), args.rval());
    597  if (NS_FAILED(rv) || args.rval().isPrimitive()) {
    598    return Throw(aCx, NS_ERROR_XPC_CANT_CREATE_WN);
    599  }
    600  return true;
    601 }
    602 
    603 static bool CID_CreateInstance(JSContext* aCx, unsigned aArgc, Value* aVp) {
    604  return CIGSHelper(aCx, aArgc, aVp, /* aGetService = */ false);
    605 }
    606 
    607 static bool CID_GetService(JSContext* aCx, unsigned aArgc, Value* aVp) {
    608  return CIGSHelper(aCx, aArgc, aVp, /* aGetService = */ true);
    609 }
    610 
    611 // NOTE: This method is used both for 'get CID.prototype.name' and
    612 // 'CID.prototype.toString'.
    613 static bool CID_GetName(JSContext* aCx, unsigned aArgc, Value* aVp) {
    614  CallArgs args = CallArgsFromVp(aArgc, aVp);
    615  RootedObject obj(aCx, GetIDObject(args.thisv(), &sCID_Class));
    616  if (!obj) {
    617    return Throw(aCx, NS_ERROR_XPC_BAD_CONVERT_JS);
    618  }
    619 
    620  // Return the string stored in our reserved ContractID slot.
    621  args.rval().set(JS::GetReservedSlot(obj, kCID_ContractSlot));
    622  return true;
    623 }
    624 
    625 }  // namespace xpc