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