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(), ®istered);
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