summaryrefslogtreecommitdiffstats
path: root/js/xpconnect/src/XPCJSID.cpp
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 17:32:43 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 17:32:43 +0000
commit6bf0a5cb5034a7e684dcc3500e841785237ce2dd (patch)
treea68f146d7fa01f0134297619fbe7e33db084e0aa /js/xpconnect/src/XPCJSID.cpp
parentInitial commit. (diff)
downloadthunderbird-6bf0a5cb5034a7e684dcc3500e841785237ce2dd.tar.xz
thunderbird-6bf0a5cb5034a7e684dcc3500e841785237ce2dd.zip
Adding upstream version 1:115.7.0.upstream/1%115.7.0upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'js/xpconnect/src/XPCJSID.cpp')
-rw-r--r--js/xpconnect/src/XPCJSID.cpp626
1 files changed, 626 insertions, 0 deletions
diff --git a/js/xpconnect/src/XPCJSID.cpp b/js/xpconnect/src/XPCJSID.cpp
new file mode 100644
index 0000000000..565dc99c86
--- /dev/null
+++ b/js/xpconnect/src/XPCJSID.cpp
@@ -0,0 +1,626 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/* An xpcom implementation of the JavaScript nsIID and nsCID objects. */
+
+#include "xpcprivate.h"
+#include "mozilla/dom/BindingUtils.h"
+#include "mozilla/Attributes.h"
+#include "js/Object.h" // JS::GetClass, JS::GetReservedSlot
+#include "js/PropertyAndElement.h" // JS_DefineFunction, JS_DefineFunctionById, JS_DefineProperty, JS_DefinePropertyById
+#include "js/Symbol.h"
+#include "nsContentUtils.h"
+
+using namespace mozilla;
+using namespace mozilla::dom;
+using namespace JS;
+
+namespace xpc {
+
+/******************************************************************************
+ * # Generic IDs #
+ *
+ * Generic IDs are the type of JS object created by most code which passes nsID
+ * objects to JavaScript code. They provide no special methods, and only store
+ * the raw nsID value.
+ *
+ * The nsID value is stored in 4 reserved slots, with 32 bits of the 128-bit
+ * value stored in each slot. Getter code extracts this data, and combines them
+ * back into the nsID value.
+ */
+static bool ID_Equals(JSContext* aCx, unsigned aArgc, Value* aVp);
+static bool ID_GetNumber(JSContext* aCx, unsigned aArgc, Value* aVp);
+
+// Generic ID objects contain 4 reserved slots, each containing a uint32_t with
+// 1/4 of the representation of the nsID value. This allows us to avoid an extra
+// allocation for the nsID object, and eliminates the need for a finalizer.
+enum { kID_Slot0, kID_Slot1, kID_Slot2, kID_Slot3, kID_SlotCount };
+static const JSClass sID_Class = {
+ "nsJSID", JSCLASS_HAS_RESERVED_SLOTS(kID_SlotCount), JS_NULL_CLASS_OPS};
+
+/******************************************************************************
+ * # Interface IDs #
+ *
+ * In addition to the properties exposed by Generic ID objects, IID supports
+ * 'instanceof', exposes constant properties defined on the class, and exposes
+ * the interface name as the 'name' and 'toString()' values.
+ */
+static bool IID_HasInstance(JSContext* aCx, unsigned aArgc, Value* aVp);
+static bool IID_GetName(JSContext* aCx, unsigned aArgc, Value* aVp);
+
+static bool IID_NewEnumerate(JSContext* cx, HandleObject obj,
+ MutableHandleIdVector properties,
+ bool enumerableOnly);
+static bool IID_Resolve(JSContext* cx, HandleObject obj, HandleId id,
+ bool* resolvedp);
+static bool IID_MayResolve(const JSAtomState& names, jsid id,
+ JSObject* maybeObj);
+
+static const JSClassOps sIID_ClassOps = {
+ nullptr, // addProperty
+ nullptr, // delProperty
+ nullptr, // enumerate
+ IID_NewEnumerate, // newEnumerate
+ IID_Resolve, // resolve
+ IID_MayResolve, // mayResolve
+ nullptr, // finalize
+ nullptr, // call
+ nullptr, // construct
+ nullptr, // trace
+};
+
+// Interface ID objects use a single reserved slot containing a pointer to the
+// nsXPTInterfaceInfo object for the interface in question.
+enum { kIID_InfoSlot, kIID_SlotCount };
+static const JSClass sIID_Class = {
+ "nsJSIID", JSCLASS_HAS_RESERVED_SLOTS(kIID_SlotCount), &sIID_ClassOps};
+
+/******************************************************************************
+ * # Contract IDs #
+ *
+ * In addition to the properties exposed by Generic ID objects, Contract IDs
+ * expose 'getService' and 'createInstance' methods, and expose the contractID
+ * string as '.name' and '.toString()'.
+ */
+static bool CID_CreateInstance(JSContext* aCx, unsigned aArgc, Value* aVp);
+static bool CID_GetService(JSContext* aCx, unsigned aArgc, Value* aVp);
+static bool CID_GetName(JSContext* aCx, unsigned aArgc, Value* aVp);
+
+// ContractID objects use a single reserved slot, containing the ContractID. The
+// nsCID value for this object is looked up when the object is being unwrapped.
+enum { kCID_ContractSlot, kCID_SlotCount };
+static const JSClass sCID_Class = {
+ "nsJSCID", JSCLASS_HAS_RESERVED_SLOTS(kCID_SlotCount), JS_NULL_CLASS_OPS};
+
+/**
+ * Ensure that the nsID prototype objects have been created for the current
+ * global, and extract the prototype values.
+ */
+static JSObject* GetIDPrototype(JSContext* aCx, const JSClass* aClass) {
+ XPCWrappedNativeScope* scope = ObjectScope(CurrentGlobalOrNull(aCx));
+ if (NS_WARN_IF(!scope)) {
+ return nullptr;
+ }
+
+ // Create prototype objects for the JSID objects if they haven't been
+ // created for this scope yet.
+ if (!scope->mIDProto) {
+ MOZ_ASSERT(!scope->mIIDProto && !scope->mCIDProto);
+
+ RootedObject idProto(aCx, JS_NewPlainObject(aCx));
+ RootedObject iidProto(aCx,
+ JS_NewObjectWithGivenProto(aCx, nullptr, idProto));
+ RootedObject cidProto(aCx,
+ JS_NewObjectWithGivenProto(aCx, nullptr, idProto));
+ RootedId hasInstance(aCx,
+ GetWellKnownSymbolKey(aCx, SymbolCode::hasInstance));
+
+ const uint32_t kFlags =
+ JSPROP_READONLY | JSPROP_ENUMERATE | JSPROP_PERMANENT;
+ const uint32_t kNoEnum = JSPROP_READONLY | JSPROP_PERMANENT;
+
+ bool ok =
+ idProto && iidProto && cidProto &&
+ // Methods and properties on all ID Objects:
+ JS_DefineFunction(aCx, idProto, "equals", ID_Equals, 1, kFlags) &&
+ JS_DefineProperty(aCx, idProto, "number", ID_GetNumber, nullptr,
+ kFlags) &&
+
+ // Methods for IfaceID objects, which also inherit ID properties:
+ JS_DefineFunctionById(aCx, iidProto, hasInstance, IID_HasInstance, 1,
+ kNoEnum) &&
+ JS_DefineProperty(aCx, iidProto, "name", IID_GetName, nullptr,
+ kFlags) &&
+
+ // Methods for ContractID objects, which also inherit ID properties:
+ JS_DefineFunction(aCx, cidProto, "createInstance", CID_CreateInstance,
+ 1, kFlags) &&
+ JS_DefineFunction(aCx, cidProto, "getService", CID_GetService, 1,
+ kFlags) &&
+ JS_DefineProperty(aCx, cidProto, "name", CID_GetName, nullptr,
+ kFlags) &&
+
+ // ToString returns '.number' on generic IDs, while returning
+ // '.name' on other ID types.
+ JS_DefineFunction(aCx, idProto, "toString", ID_GetNumber, 0, kFlags) &&
+ JS_DefineFunction(aCx, iidProto, "toString", IID_GetName, 0, kFlags) &&
+ JS_DefineFunction(aCx, cidProto, "toString", CID_GetName, 0, kFlags);
+ if (!ok) {
+ return nullptr;
+ }
+
+ scope->mIDProto = idProto;
+ scope->mIIDProto = iidProto;
+ scope->mCIDProto = cidProto;
+ }
+
+ if (aClass == &sID_Class) {
+ return scope->mIDProto;
+ } else if (aClass == &sIID_Class) {
+ return scope->mIIDProto;
+ } else if (aClass == &sCID_Class) {
+ return scope->mCIDProto;
+ }
+
+ MOZ_CRASH("Unrecognized ID Object Class");
+}
+
+// Unwrap the given value to an object with the correct class, or nullptr.
+static JSObject* GetIDObject(HandleValue aVal, const JSClass* aClass) {
+ if (aVal.isObject()) {
+ // We care only about IID/CID objects here, so CheckedUnwrapStatic is fine.
+ JSObject* obj = js::CheckedUnwrapStatic(&aVal.toObject());
+ if (obj && JS::GetClass(obj) == aClass) {
+ return obj;
+ }
+ }
+ return nullptr;
+}
+
+static const nsXPTInterfaceInfo* GetInterfaceInfo(JSObject* obj) {
+ MOZ_ASSERT(JS::GetClass(obj) == &sIID_Class);
+ return static_cast<const nsXPTInterfaceInfo*>(
+ JS::GetReservedSlot(obj, kIID_InfoSlot).toPrivate());
+}
+
+/**
+ * Unwrap an nsID object from a JSValue.
+ *
+ * For Generic ID objects, this function will extract the nsID from reserved
+ * slots. For IfaceID objects, it will be extracted from the nsXPTInterfaceInfo,
+ * and for ContractID objects, the ContractID's corresponding CID will be looked
+ * up.
+ */
+Maybe<nsID> JSValue2ID(JSContext* aCx, HandleValue aVal) {
+ if (!aVal.isObject()) {
+ return Nothing();
+ }
+
+ // We only care about ID objects here, so CheckedUnwrapStatic is fine.
+ RootedObject obj(aCx, js::CheckedUnwrapStatic(&aVal.toObject()));
+ if (!obj) {
+ return Nothing();
+ }
+
+ mozilla::Maybe<nsID> id;
+ if (JS::GetClass(obj) == &sID_Class) {
+ // Extract the raw bytes of the nsID from reserved slots.
+ uint32_t rawid[] = {JS::GetReservedSlot(obj, kID_Slot0).toPrivateUint32(),
+ JS::GetReservedSlot(obj, kID_Slot1).toPrivateUint32(),
+ JS::GetReservedSlot(obj, kID_Slot2).toPrivateUint32(),
+ JS::GetReservedSlot(obj, kID_Slot3).toPrivateUint32()};
+
+ // Construct a nsID inside the Maybe, and copy the rawid into it.
+ id.emplace();
+ memcpy(id.ptr(), &rawid, sizeof(nsID));
+ } else if (JS::GetClass(obj) == &sIID_Class) {
+ // IfaceID objects store a nsXPTInterfaceInfo* pointer.
+ const nsXPTInterfaceInfo* info = GetInterfaceInfo(obj);
+ id.emplace(info->IID());
+ } else if (JS::GetClass(obj) == &sCID_Class) {
+ // ContractID objects store a ContractID string.
+ JS::UniqueChars contractId = JS_EncodeStringToLatin1(
+ aCx, JS::GetReservedSlot(obj, kCID_ContractSlot).toString());
+
+ // NOTE(nika): If we directly access the nsComponentManager, we can do
+ // this with a more-basic pointer lookup:
+ // nsFactoryEntry* entry = nsComponentManagerImpl::gComponentManager->
+ // GetFactoryEntry(contractId.ptr(), contractId.length());
+ // if (entry) id.emplace(entry->mCIDEntry->cid);
+
+ nsCOMPtr<nsIComponentRegistrar> registrar;
+ nsresult rv = NS_GetComponentRegistrar(getter_AddRefs(registrar));
+ if (NS_FAILED(rv) || !registrar) {
+ return Nothing();
+ }
+
+ nsCID* cid = nullptr;
+ if (NS_SUCCEEDED(registrar->ContractIDToCID(contractId.get(), &cid))) {
+ id.emplace(*cid);
+ free(cid);
+ }
+ }
+ return id;
+}
+
+/**
+ * Public ID Object Constructor Methods
+ */
+static JSObject* NewIDObjectHelper(JSContext* aCx, const JSClass* aClass) {
+ RootedObject proto(aCx, GetIDPrototype(aCx, aClass));
+ if (proto) {
+ return JS_NewObjectWithGivenProto(aCx, aClass, proto);
+ }
+ return nullptr;
+}
+
+bool ID2JSValue(JSContext* aCx, const nsID& aId, MutableHandleValue aVal) {
+ RootedObject obj(aCx, NewIDObjectHelper(aCx, &sID_Class));
+ if (!obj) {
+ return false;
+ }
+
+ // Get the data in nsID as 4 uint32_ts, and store them in slots.
+ uint32_t rawid[4];
+ memcpy(&rawid, &aId, sizeof(nsID));
+ static_assert(sizeof(nsID) == sizeof(rawid), "Wrong size of nsID");
+ JS::SetReservedSlot(obj, kID_Slot0, PrivateUint32Value(rawid[0]));
+ JS::SetReservedSlot(obj, kID_Slot1, PrivateUint32Value(rawid[1]));
+ JS::SetReservedSlot(obj, kID_Slot2, PrivateUint32Value(rawid[2]));
+ JS::SetReservedSlot(obj, kID_Slot3, PrivateUint32Value(rawid[3]));
+
+ aVal.setObject(*obj);
+ return true;
+}
+
+bool IfaceID2JSValue(JSContext* aCx, const nsXPTInterfaceInfo& aInfo,
+ MutableHandleValue aVal) {
+ RootedObject obj(aCx, NewIDObjectHelper(aCx, &sIID_Class));
+ if (!obj) {
+ return false;
+ }
+
+ // The InterfaceInfo is stored in a reserved slot.
+ JS::SetReservedSlot(obj, kIID_InfoSlot, PrivateValue((void*)&aInfo));
+ aVal.setObject(*obj);
+ return true;
+}
+
+bool ContractID2JSValue(JSContext* aCx, JSString* aContract,
+ MutableHandleValue aVal) {
+ RootedString jsContract(aCx, aContract);
+
+ {
+ // It is perfectly safe to have a ContractID object with an invalid
+ // ContractID, but is usually a bug.
+ nsCOMPtr<nsIComponentRegistrar> registrar;
+ NS_GetComponentRegistrar(getter_AddRefs(registrar));
+ if (!registrar) {
+ return false;
+ }
+
+ bool registered = false;
+ JS::UniqueChars contract = JS_EncodeStringToLatin1(aCx, jsContract);
+ registrar->IsContractIDRegistered(contract.get(), &registered);
+ if (!registered) {
+ return false;
+ }
+ }
+
+ RootedObject obj(aCx, NewIDObjectHelper(aCx, &sCID_Class));
+ if (!obj) {
+ return false;
+ }
+
+ // The Contract is stored in a reserved slot.
+ JS::SetReservedSlot(obj, kCID_ContractSlot, StringValue(jsContract));
+ aVal.setObject(*obj);
+ return true;
+}
+
+/******************************************************************************
+ * # Method & Property Getter Implementations #
+ */
+
+// NOTE: This method is used both for 'get ID.prototype.number' and
+// 'ID.prototype.toString'.
+static bool ID_GetNumber(JSContext* aCx, unsigned aArgc, Value* aVp) {
+ CallArgs args = CallArgsFromVp(aArgc, aVp);
+
+ Maybe<nsID> id = JSValue2ID(aCx, args.thisv());
+ if (!id) {
+ return Throw(aCx, NS_ERROR_XPC_BAD_CONVERT_JS);
+ }
+
+ char buf[NSID_LENGTH];
+ id->ToProvidedString(buf);
+ JSString* jsnum = JS_NewStringCopyZ(aCx, buf);
+ if (!jsnum) {
+ return Throw(aCx, NS_ERROR_OUT_OF_MEMORY);
+ }
+
+ args.rval().setString(jsnum);
+ return true;
+}
+
+static bool ID_Equals(JSContext* aCx, unsigned aArgc, Value* aVp) {
+ CallArgs args = CallArgsFromVp(aArgc, aVp);
+ if (!args.requireAtLeast(aCx, "nsID.equals", 1)) {
+ return false;
+ }
+
+ Maybe<nsID> id = JSValue2ID(aCx, args.thisv());
+ Maybe<nsID> id2 = JSValue2ID(aCx, args[0]);
+ if (!id || !id2) {
+ return Throw(aCx, NS_ERROR_XPC_BAD_CONVERT_JS);
+ }
+
+ args.rval().setBoolean(id->Equals(*id2));
+ return true;
+}
+
+/*
+ * HasInstance hooks need to find an appropriate reflector in order to function
+ * properly. There are two complexities that we need to handle:
+ *
+ * 1 - Cross-compartment wrappers. Chrome uses over 100 compartments, all with
+ * system principal. The success of an instanceof check should not depend
+ * on which compartment an object comes from. At the same time, we want to
+ * make sure we don't unwrap important security wrappers.
+ * CheckedUnwrap does the right thing here.
+ *
+ * 2 - Prototype chains. Suppose someone creates a vanilla JS object |a| and
+ * sets its __proto__ to some WN |b|. If |b instanceof nsIFoo| returns true,
+ * one would expect |a instanceof nsIFoo| to return true as well, since
+ * instanceof is transitive up the prototype chain in ECMAScript. Moreover,
+ * there's chrome code that relies on this.
+ *
+ * This static method handles both complexities, returning either an XPCWN, a
+ * DOM object, or null. The object may well be cross-compartment from |cx|.
+ */
+static nsresult FindObjectForHasInstance(JSContext* cx, HandleObject objArg,
+ MutableHandleObject target) {
+ RootedObject obj(cx, objArg), proto(cx);
+ while (true) {
+ // Try the object, or the wrappee if allowed. We want CheckedUnwrapDynamic
+ // here, because we might in fact be looking for a Window. "cx" represents
+ // our current global.
+ JSObject* o =
+ js::IsWrapper(obj) ? js::CheckedUnwrapDynamic(obj, cx, false) : obj;
+ if (o && (IsWrappedNativeReflector(o) || IsDOMObject(o))) {
+ target.set(o);
+ return NS_OK;
+ }
+
+ // Walk the prototype chain from the perspective of the callee (i.e.
+ // respecting Xrays if they exist).
+ if (!js::GetObjectProto(cx, obj, &proto)) {
+ return NS_ERROR_FAILURE;
+ }
+ if (!proto) {
+ target.set(nullptr);
+ return NS_OK;
+ }
+ obj = proto;
+ }
+}
+
+nsresult HasInstance(JSContext* cx, HandleObject objArg, const nsID* iid,
+ bool* bp) {
+ *bp = false;
+
+ RootedObject obj(cx);
+ nsresult rv = FindObjectForHasInstance(cx, objArg, &obj);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ if (!obj) {
+ return NS_OK;
+ }
+
+ // Need to unwrap Window correctly here, so use ReflectorToISupportsDynamic.
+ nsCOMPtr<nsISupports> identity = ReflectorToISupportsDynamic(obj, cx);
+ if (!identity) {
+ return NS_OK;
+ }
+
+ nsCOMPtr<nsISupports> supp;
+ identity->QueryInterface(*iid, getter_AddRefs(supp));
+ *bp = supp;
+
+ // Our old HasInstance implementation operated by invoking FindTearOff on
+ // XPCWrappedNatives, and various bits of chrome JS came to depend on
+ // |instanceof| doing an implicit QI if it succeeds. Do a drive-by QI to
+ // preserve that behavior. This is just a compatibility hack, so we don't
+ // really care if it fails.
+ if (IsWrappedNativeReflector(obj)) {
+ (void)XPCWrappedNative::Get(obj)->FindTearOff(cx, *iid);
+ }
+
+ return NS_OK;
+}
+
+static bool IID_HasInstance(JSContext* aCx, unsigned aArgc, Value* aVp) {
+ CallArgs args = CallArgsFromVp(aArgc, aVp);
+ if (!args.requireAtLeast(aCx, "nsIID[Symbol.hasInstance]", 1)) {
+ return false;
+ }
+
+ Maybe<nsID> id = JSValue2ID(aCx, args.thisv());
+ if (!id) {
+ return Throw(aCx, NS_ERROR_XPC_BAD_CONVERT_JS);
+ }
+
+ bool hasInstance = false;
+ if (args[0].isObject()) {
+ RootedObject target(aCx, &args[0].toObject());
+ nsresult rv = HasInstance(aCx, target, id.ptr(), &hasInstance);
+ if (NS_FAILED(rv)) {
+ return Throw(aCx, rv);
+ }
+ }
+ args.rval().setBoolean(hasInstance);
+ return true;
+}
+
+// NOTE: This method is used both for 'get IID.prototype.name' and
+// 'IID.prototype.toString'.
+static bool IID_GetName(JSContext* aCx, unsigned aArgc, Value* aVp) {
+ CallArgs args = CallArgsFromVp(aArgc, aVp);
+
+ RootedObject obj(aCx, GetIDObject(args.thisv(), &sIID_Class));
+ if (!obj) {
+ return Throw(aCx, NS_ERROR_XPC_BAD_CONVERT_JS);
+ }
+
+ const nsXPTInterfaceInfo* info = GetInterfaceInfo(obj);
+
+ // Name property is the name of the interface this nsIID was created from.
+ JSString* name = JS_NewStringCopyZ(aCx, info->Name());
+ if (!name) {
+ return Throw(aCx, NS_ERROR_OUT_OF_MEMORY);
+ }
+
+ args.rval().setString(name);
+ return true;
+}
+
+static bool IID_NewEnumerate(JSContext* cx, HandleObject obj,
+ MutableHandleIdVector properties,
+ bool enumerableOnly) {
+ const nsXPTInterfaceInfo* info = GetInterfaceInfo(obj);
+
+ if (!properties.reserve(info->ConstantCount())) {
+ JS_ReportOutOfMemory(cx);
+ return false;
+ }
+
+ RootedId id(cx);
+ RootedString name(cx);
+ for (uint16_t i = 0; i < info->ConstantCount(); ++i) {
+ name = JS_AtomizeString(cx, info->Constant(i).Name());
+ if (!name || !JS_StringToId(cx, name, &id)) {
+ return false;
+ }
+ properties.infallibleAppend(id);
+ }
+
+ return true;
+}
+
+static bool IID_Resolve(JSContext* cx, HandleObject obj, HandleId id,
+ bool* resolvedp) {
+ *resolvedp = false;
+ if (!id.isString()) {
+ return true;
+ }
+
+ JSLinearString* name = id.toLinearString();
+ const nsXPTInterfaceInfo* info = GetInterfaceInfo(obj);
+ for (uint16_t i = 0; i < info->ConstantCount(); ++i) {
+ if (JS_LinearStringEqualsAscii(name, info->Constant(i).Name())) {
+ *resolvedp = true;
+
+ RootedValue constant(cx, info->Constant(i).JSValue());
+ return JS_DefinePropertyById(
+ cx, obj, id, constant,
+ JSPROP_READONLY | JSPROP_ENUMERATE | JSPROP_PERMANENT);
+ }
+ }
+ return true;
+}
+
+static bool IID_MayResolve(const JSAtomState& names, jsid id,
+ JSObject* maybeObj) {
+ if (!id.isString()) {
+ return false;
+ }
+
+ if (!maybeObj) {
+ // Each interface object has its own set of constants, so if we don't know
+ // the object, assume any string property may be resolved.
+ return true;
+ }
+
+ JSLinearString* name = id.toLinearString();
+ const nsXPTInterfaceInfo* info = GetInterfaceInfo(maybeObj);
+ for (uint16_t i = 0; i < info->ConstantCount(); ++i) {
+ if (JS_LinearStringEqualsAscii(name, info->Constant(i).Name())) {
+ return true;
+ }
+ }
+ return false;
+}
+
+// Common code for CID_CreateInstance and CID_GetService
+static bool CIGSHelper(JSContext* aCx, unsigned aArgc, Value* aVp,
+ bool aGetService) {
+ CallArgs args = CallArgsFromVp(aArgc, aVp);
+
+ // Extract the ContractID string from our reserved slot. Don't use
+ // JSValue2ID as this method should only be defined on Contract ID objects,
+ // and it allows us to avoid a duplicate hashtable lookup.
+ RootedObject obj(aCx, GetIDObject(args.thisv(), &sCID_Class));
+ if (!obj) {
+ return Throw(aCx, NS_ERROR_XPC_BAD_CONVERT_JS);
+ }
+ JS::UniqueChars contractID = JS_EncodeStringToLatin1(
+ aCx, JS::GetReservedSlot(obj, kCID_ContractSlot).toString());
+
+ // Extract the IID from the first argument, if passed. Default: nsISupports.
+ Maybe<nsIID> iid = args.length() >= 1 ? JSValue2ID(aCx, args[0])
+ : Some(NS_GET_IID(nsISupports));
+ if (!iid) {
+ return Throw(aCx, NS_ERROR_XPC_BAD_CONVERT_JS);
+ }
+
+ // Invoke CreateInstance or GetService with our ContractID.
+ nsresult rv;
+ nsCOMPtr<nsISupports> result;
+ if (aGetService) {
+ rv = CallGetService(contractID.get(), *iid, getter_AddRefs(result));
+ if (NS_FAILED(rv) || !result) {
+ return Throw(aCx, NS_ERROR_XPC_GS_RETURNED_FAILURE);
+ }
+ } else {
+ rv = CallCreateInstance(contractID.get(), *iid, getter_AddRefs(result));
+ if (NS_FAILED(rv) || !result) {
+ return Throw(aCx, NS_ERROR_XPC_CI_RETURNED_FAILURE);
+ }
+ }
+
+ // Wrap the created object and return it.
+ rv = nsContentUtils::WrapNative(aCx, result, iid.ptr(), args.rval());
+ if (NS_FAILED(rv) || args.rval().isPrimitive()) {
+ return Throw(aCx, NS_ERROR_XPC_CANT_CREATE_WN);
+ }
+ return true;
+}
+
+static bool CID_CreateInstance(JSContext* aCx, unsigned aArgc, Value* aVp) {
+ return CIGSHelper(aCx, aArgc, aVp, /* aGetService = */ false);
+}
+
+static bool CID_GetService(JSContext* aCx, unsigned aArgc, Value* aVp) {
+ return CIGSHelper(aCx, aArgc, aVp, /* aGetService = */ true);
+}
+
+// NOTE: This method is used both for 'get CID.prototype.name' and
+// 'CID.prototype.toString'.
+static bool CID_GetName(JSContext* aCx, unsigned aArgc, Value* aVp) {
+ CallArgs args = CallArgsFromVp(aArgc, aVp);
+ RootedObject obj(aCx, GetIDObject(args.thisv(), &sCID_Class));
+ if (!obj) {
+ return Throw(aCx, NS_ERROR_XPC_BAD_CONVERT_JS);
+ }
+
+ // Return the string stored in our reserved ContractID slot.
+ args.rval().set(JS::GetReservedSlot(obj, kCID_ContractSlot));
+ return true;
+}
+
+} // namespace xpc