summaryrefslogtreecommitdiffstats
path: root/js/xpconnect/src/XPCJSID.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'js/xpconnect/src/XPCJSID.cpp')
-rw-r--r--js/xpconnect/src/XPCJSID.cpp816
1 files changed, 816 insertions, 0 deletions
diff --git a/js/xpconnect/src/XPCJSID.cpp b/js/xpconnect/src/XPCJSID.cpp
new file mode 100644
index 000000000..b9cbee7be
--- /dev/null
+++ b/js/xpconnect/src/XPCJSID.cpp
@@ -0,0 +1,816 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* vim: set ts=8 sts=4 et sw=4 tw=99: */
+/* 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 "xpc_make_class.h"
+#include "mozilla/dom/BindingUtils.h"
+#include "mozilla/Attributes.h"
+#include "mozilla/jsipc/CrossProcessObjectWrappers.h"
+#include "mozilla/StaticPtr.h"
+
+using namespace mozilla::dom;
+using namespace JS;
+
+/***************************************************************************/
+// nsJSID
+
+NS_IMPL_CLASSINFO(nsJSID, nullptr, 0, NS_JS_ID_CID)
+NS_IMPL_ISUPPORTS_CI(nsJSID, nsIJSID)
+
+const char nsJSID::gNoString[] = "";
+
+nsJSID::nsJSID()
+ : mID(GetInvalidIID()),
+ mNumber(const_cast<char*>(gNoString)),
+ mName(const_cast<char*>(gNoString))
+{
+}
+
+nsJSID::~nsJSID()
+{
+ if (mNumber && mNumber != gNoString)
+ free(mNumber);
+ if (mName && mName != gNoString)
+ free(mName);
+}
+
+void nsJSID::Reset()
+{
+ mID = GetInvalidIID();
+
+ if (mNumber && mNumber != gNoString)
+ free(mNumber);
+ if (mName && mName != gNoString)
+ free(mName);
+
+ mNumber = mName = nullptr;
+}
+
+bool
+nsJSID::SetName(const char* name)
+{
+ MOZ_ASSERT(!mName || mName == gNoString ,"name already set");
+ MOZ_ASSERT(name,"null name");
+ mName = NS_strdup(name);
+ return mName ? true : false;
+}
+
+NS_IMETHODIMP
+nsJSID::GetName(char * *aName)
+{
+ if (!aName)
+ return NS_ERROR_NULL_POINTER;
+
+ if (!NameIsSet())
+ SetNameToNoString();
+ MOZ_ASSERT(mName, "name not set");
+ *aName = NS_strdup(mName);
+ return *aName ? NS_OK : NS_ERROR_OUT_OF_MEMORY;
+}
+
+NS_IMETHODIMP
+nsJSID::GetNumber(char * *aNumber)
+{
+ if (!aNumber)
+ return NS_ERROR_NULL_POINTER;
+
+ if (!mNumber) {
+ if (!(mNumber = mID.ToString()))
+ mNumber = const_cast<char*>(gNoString);
+ }
+
+ *aNumber = NS_strdup(mNumber);
+ return *aNumber ? NS_OK : NS_ERROR_OUT_OF_MEMORY;
+}
+
+NS_IMETHODIMP_(const nsID*)
+nsJSID::GetID()
+{
+ return &mID;
+}
+
+NS_IMETHODIMP
+nsJSID::GetValid(bool* aValid)
+{
+ if (!aValid)
+ return NS_ERROR_NULL_POINTER;
+
+ *aValid = IsValid();
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsJSID::Equals(nsIJSID* other, bool* _retval)
+{
+ if (!_retval)
+ return NS_ERROR_NULL_POINTER;
+
+ if (!other || mID.Equals(GetInvalidIID())) {
+ *_retval = false;
+ return NS_OK;
+ }
+
+ *_retval = other->GetID()->Equals(mID);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsJSID::Initialize(const char* idString)
+{
+ if (!idString)
+ return NS_ERROR_NULL_POINTER;
+
+ if (*idString != '\0' && mID.Equals(GetInvalidIID())) {
+ Reset();
+
+ if (idString[0] == '{') {
+ if (mID.Parse(idString)) {
+ return NS_OK;
+ }
+
+ // error - reset to invalid state
+ mID = GetInvalidIID();
+ }
+ }
+ return NS_ERROR_FAILURE;
+}
+
+bool
+nsJSID::InitWithName(const nsID& id, const char* nameString)
+{
+ MOZ_ASSERT(nameString, "no name");
+ Reset();
+ mID = id;
+ return SetName(nameString);
+}
+
+// try to use the name, if no name, then use the number
+NS_IMETHODIMP
+nsJSID::ToString(char** _retval)
+{
+ if (mName && mName != gNoString)
+ return GetName(_retval);
+
+ return GetNumber(_retval);
+}
+
+const nsID&
+nsJSID::GetInvalidIID() const
+{
+ // {BB1F47B0-D137-11d2-9841-006008962422}
+ static const nsID invalid = {0xbb1f47b0, 0xd137, 0x11d2,
+ {0x98, 0x41, 0x0, 0x60, 0x8, 0x96, 0x24, 0x22}};
+ return invalid;
+}
+
+//static
+already_AddRefed<nsJSID>
+nsJSID::NewID(const char* str)
+{
+ if (!str) {
+ NS_ERROR("no string");
+ return nullptr;
+ }
+
+ RefPtr<nsJSID> idObj = new nsJSID();
+ NS_ENSURE_SUCCESS(idObj->Initialize(str), nullptr);
+ return idObj.forget();
+}
+
+//static
+already_AddRefed<nsJSID>
+nsJSID::NewID(const nsID& id)
+{
+ RefPtr<nsJSID> idObj = new nsJSID();
+ idObj->mID = id;
+ idObj->mName = nullptr;
+ idObj->mNumber = nullptr;
+ return idObj.forget();
+}
+
+
+/***************************************************************************/
+// Class object support so that we can share prototypes of wrapper
+
+// This class exists just so we can have a shared scriptable helper for
+// the nsJSIID class. The instances implement their own helpers. But we
+// needed to be able to indicate to the shared prototypes this single flag:
+// nsIXPCScriptable::DONT_ENUM_STATIC_PROPS. And having a class to do it is
+// the only means we have. Setting this flag on any given instance scriptable
+// helper is not sufficient to convey the information that we don't want
+// static properties enumerated on the shared proto.
+
+class SharedScriptableHelperForJSIID final : public nsIXPCScriptable
+{
+ ~SharedScriptableHelperForJSIID() {}
+public:
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIXPCSCRIPTABLE
+ SharedScriptableHelperForJSIID() {}
+};
+
+NS_INTERFACE_MAP_BEGIN(SharedScriptableHelperForJSIID)
+ NS_INTERFACE_MAP_ENTRY(nsIXPCScriptable)
+ NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIXPCScriptable)
+NS_INTERFACE_MAP_END
+
+NS_IMPL_ADDREF(SharedScriptableHelperForJSIID)
+NS_IMPL_RELEASE(SharedScriptableHelperForJSIID)
+
+// The nsIXPCScriptable map declaration that will generate stubs for us...
+#define XPC_MAP_CLASSNAME SharedScriptableHelperForJSIID
+#define XPC_MAP_QUOTED_CLASSNAME "JSIID"
+#define XPC_MAP_FLAGS nsIXPCScriptable::ALLOW_PROP_MODS_DURING_RESOLVE
+#include "xpc_map_end.h" /* This will #undef the above */
+
+static mozilla::StaticRefPtr<nsIXPCScriptable> gSharedScriptableHelperForJSIID;
+static bool gClassObjectsWereInited = false;
+
+static void EnsureClassObjectsInitialized()
+{
+ if (!gClassObjectsWereInited) {
+ gSharedScriptableHelperForJSIID = new SharedScriptableHelperForJSIID();
+
+ gClassObjectsWereInited = true;
+ }
+}
+
+static nsresult GetSharedScriptableHelperForJSIID(nsIXPCScriptable** helper)
+{
+ EnsureClassObjectsInitialized();
+ nsCOMPtr<nsIXPCScriptable> temp = gSharedScriptableHelperForJSIID.get();
+ temp.forget(helper);
+ return NS_OK;
+}
+
+/******************************************************/
+
+#define NULL_CID \
+{ 0x00000000, 0x0000, 0x0000, \
+ { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } }
+
+// We pass nsIClassInfo::DOM_OBJECT so that nsJSIID instances may be created
+// in unprivileged scopes.
+NS_DECL_CI_INTERFACE_GETTER(nsJSIID)
+NS_IMPL_CLASSINFO(nsJSIID, GetSharedScriptableHelperForJSIID,
+ nsIClassInfo::DOM_OBJECT, NULL_CID)
+
+NS_DECL_CI_INTERFACE_GETTER(nsJSCID)
+NS_IMPL_CLASSINFO(nsJSCID, nullptr, 0, NULL_CID)
+
+void xpc_DestroyJSxIDClassObjects()
+{
+ if (gClassObjectsWereInited) {
+ NS_IF_RELEASE(NS_CLASSINFO_NAME(nsJSIID));
+ NS_IF_RELEASE(NS_CLASSINFO_NAME(nsJSCID));
+ gSharedScriptableHelperForJSIID = nullptr;
+
+ gClassObjectsWereInited = false;
+ }
+}
+
+/***************************************************************************/
+
+NS_INTERFACE_MAP_BEGIN(nsJSIID)
+ NS_INTERFACE_MAP_ENTRY(nsIJSID)
+ NS_INTERFACE_MAP_ENTRY(nsIJSIID)
+ NS_INTERFACE_MAP_ENTRY(nsIXPCScriptable)
+ NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIJSID)
+ NS_IMPL_QUERY_CLASSINFO(nsJSIID)
+NS_INTERFACE_MAP_END
+
+NS_IMPL_ADDREF(nsJSIID)
+NS_IMPL_RELEASE(nsJSIID)
+NS_IMPL_CI_INTERFACE_GETTER(nsJSIID, nsIJSID, nsIJSIID)
+
+// The nsIXPCScriptable map declaration that will generate stubs for us...
+#define XPC_MAP_CLASSNAME nsJSIID
+#define XPC_MAP_QUOTED_CLASSNAME "nsJSIID"
+#define XPC_MAP_WANT_RESOLVE
+#define XPC_MAP_WANT_ENUMERATE
+#define XPC_MAP_WANT_HASINSTANCE
+#define XPC_MAP_FLAGS nsIXPCScriptable::ALLOW_PROP_MODS_DURING_RESOLVE
+#include "xpc_map_end.h" /* This will #undef the above */
+
+
+nsJSIID::nsJSIID(nsIInterfaceInfo* aInfo)
+ : mInfo(aInfo)
+{
+}
+
+nsJSIID::~nsJSIID() {}
+
+// If mInfo is present we use it and ignore mDetails, else we use mDetails.
+
+NS_IMETHODIMP nsJSIID::GetName(char * *aName)
+{
+ return mInfo->GetName(aName);
+}
+
+NS_IMETHODIMP nsJSIID::GetNumber(char * *aNumber)
+{
+ char str[NSID_LENGTH];
+ const nsIID* id;
+ mInfo->GetIIDShared(&id);
+ id->ToProvidedString(str);
+ *aNumber = (char*) nsMemory::Clone(str, NSID_LENGTH);
+ return *aNumber ? NS_OK : NS_ERROR_OUT_OF_MEMORY;
+}
+
+NS_IMETHODIMP_(const nsID*) nsJSIID::GetID()
+{
+ const nsIID* id;
+ mInfo->GetIIDShared(&id);
+ return id;
+}
+
+NS_IMETHODIMP nsJSIID::GetValid(bool* aValid)
+{
+ *aValid = true;
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsJSIID::Equals(nsIJSID* other, bool* _retval)
+{
+ if (!_retval)
+ return NS_ERROR_NULL_POINTER;
+
+ if (!other) {
+ *_retval = false;
+ return NS_OK;
+ }
+
+ mInfo->IsIID(other->GetID(), _retval);
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsJSIID::Initialize(const char* idString)
+{
+ return NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP nsJSIID::ToString(char** _retval)
+{
+ return mInfo->GetName(_retval);
+}
+
+// static
+already_AddRefed<nsJSIID>
+nsJSIID::NewID(nsIInterfaceInfo* aInfo)
+{
+ if (!aInfo) {
+ NS_ERROR("no info");
+ return nullptr;
+ }
+
+ bool canScript;
+ if (NS_FAILED(aInfo->IsScriptable(&canScript)) || !canScript)
+ return nullptr;
+
+ RefPtr<nsJSIID> idObj = new nsJSIID(aInfo);
+ return idObj.forget();
+}
+
+
+NS_IMETHODIMP
+nsJSIID::Resolve(nsIXPConnectWrappedNative* wrapper,
+ JSContext * cx, JSObject * objArg,
+ jsid idArg, bool* resolvedp,
+ bool* _retval)
+{
+ RootedObject obj(cx, objArg);
+ RootedId id(cx, idArg);
+ XPCCallContext ccx(cx);
+
+ RefPtr<XPCNativeInterface> iface =
+ XPCNativeInterface::GetNewOrUsed(mInfo);
+
+ if (!iface)
+ return NS_OK;
+
+ XPCNativeMember* member = iface->FindMember(id);
+ if (member && member->IsConstant()) {
+ RootedValue val(cx);
+ if (!member->GetConstantValue(ccx, iface, val.address()))
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ *resolvedp = true;
+ *_retval = JS_DefinePropertyById(cx, obj, id, val,
+ JSPROP_ENUMERATE | JSPROP_READONLY |
+ JSPROP_PERMANENT | JSPROP_RESOLVING);
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsJSIID::Enumerate(nsIXPConnectWrappedNative* wrapper,
+ JSContext * cx, JSObject * objArg, bool* _retval)
+{
+ // In this case, let's just eagerly resolve...
+
+ RootedObject obj(cx, objArg);
+ XPCCallContext ccx(cx);
+
+ RefPtr<XPCNativeInterface> iface =
+ XPCNativeInterface::GetNewOrUsed(mInfo);
+
+ if (!iface)
+ return NS_OK;
+
+ uint16_t count = iface->GetMemberCount();
+ for (uint16_t i = 0; i < count; i++) {
+ XPCNativeMember* member = iface->GetMemberAt(i);
+ if (member && member->IsConstant() &&
+ !xpc_ForcePropertyResolve(cx, obj, member->GetName())) {
+ return NS_ERROR_UNEXPECTED;
+ }
+ }
+ return NS_OK;
+}
+
+/*
+ * 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 (obj && !IS_WN_REFLECTOR(obj) &&
+ !IsDOMObject(obj) && !mozilla::jsipc::IsCPOW(obj))
+ {
+ if (js::IsWrapper(obj)) {
+ obj = js::CheckedUnwrap(obj, /* stopAtWindowProxy = */ false);
+ continue;
+ }
+
+ {
+ JSAutoCompartment ac(cx, obj);
+ if (!js::GetObjectProto(cx, obj, &proto))
+ return NS_ERROR_FAILURE;
+ }
+
+ obj = proto;
+ }
+
+ target.set(obj);
+ return NS_OK;
+}
+
+nsresult
+xpc::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;
+
+ if (mozilla::jsipc::IsCPOW(obj))
+ return mozilla::jsipc::InstanceOf(obj, iid, bp);
+
+ nsCOMPtr<nsISupports> identity = UnwrapReflectorToISupports(obj);
+ 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 (IS_WN_REFLECTOR(obj))
+ (void) XPCWrappedNative::Get(obj)->FindTearOff(*iid);
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsJSIID::HasInstance(nsIXPConnectWrappedNative* wrapper,
+ JSContext* cx, JSObject * /* unused */,
+ HandleValue val, bool* bp, bool* _retval)
+{
+ *bp = false;
+
+ if (val.isPrimitive())
+ return NS_OK;
+
+ // we have a JSObject
+ RootedObject obj(cx, &val.toObject());
+
+ const nsIID* iid;
+ mInfo->GetIIDShared(&iid);
+ return xpc::HasInstance(cx, obj, iid, bp);
+}
+
+/***************************************************************************/
+
+NS_INTERFACE_MAP_BEGIN(nsJSCID)
+ NS_INTERFACE_MAP_ENTRY(nsIJSID)
+ NS_INTERFACE_MAP_ENTRY(nsIJSCID)
+ NS_INTERFACE_MAP_ENTRY(nsIXPCScriptable)
+ NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIJSID)
+ NS_IMPL_QUERY_CLASSINFO(nsJSCID)
+NS_INTERFACE_MAP_END
+
+NS_IMPL_ADDREF(nsJSCID)
+NS_IMPL_RELEASE(nsJSCID)
+NS_IMPL_CI_INTERFACE_GETTER(nsJSCID, nsIJSID, nsIJSCID)
+
+// The nsIXPCScriptable map declaration that will generate stubs for us...
+#define XPC_MAP_CLASSNAME nsJSCID
+#define XPC_MAP_QUOTED_CLASSNAME "nsJSCID"
+#define XPC_MAP_WANT_CONSTRUCT
+#define XPC_MAP_WANT_HASINSTANCE
+#define XPC_MAP_FLAGS 0
+#include "xpc_map_end.h" /* This will #undef the above */
+
+nsJSCID::nsJSCID() { mDetails = new nsJSID(); }
+nsJSCID::~nsJSCID() {}
+
+NS_IMETHODIMP nsJSCID::GetName(char * *aName)
+ {ResolveName(); return mDetails->GetName(aName);}
+
+NS_IMETHODIMP nsJSCID::GetNumber(char * *aNumber)
+ {return mDetails->GetNumber(aNumber);}
+
+NS_IMETHODIMP_(const nsID*) nsJSCID::GetID()
+ {return &mDetails->ID();}
+
+NS_IMETHODIMP nsJSCID::GetValid(bool* aValid)
+ {return mDetails->GetValid(aValid);}
+
+NS_IMETHODIMP nsJSCID::Equals(nsIJSID* other, bool* _retval)
+ {return mDetails->Equals(other, _retval);}
+
+NS_IMETHODIMP nsJSCID::Initialize(const char* idString)
+ {return mDetails->Initialize(idString);}
+
+NS_IMETHODIMP nsJSCID::ToString(char** _retval)
+ {ResolveName(); return mDetails->ToString(_retval);}
+
+void
+nsJSCID::ResolveName()
+{
+ if (!mDetails->NameIsSet())
+ mDetails->SetNameToNoString();
+}
+
+//static
+already_AddRefed<nsJSCID>
+nsJSCID::NewID(const char* str)
+{
+ if (!str) {
+ NS_ERROR("no string");
+ return nullptr;
+ }
+
+ RefPtr<nsJSCID> idObj = new nsJSCID();
+ if (str[0] == '{') {
+ NS_ENSURE_SUCCESS(idObj->Initialize(str), nullptr);
+ } else {
+ nsCOMPtr<nsIComponentRegistrar> registrar;
+ NS_GetComponentRegistrar(getter_AddRefs(registrar));
+ NS_ENSURE_TRUE(registrar, nullptr);
+
+ nsCID* cid;
+ if (NS_FAILED(registrar->ContractIDToCID(str, &cid)))
+ return nullptr;
+ bool success = idObj->mDetails->InitWithName(*cid, str);
+ free(cid);
+ if (!success)
+ return nullptr;
+ }
+ return idObj.forget();
+}
+
+static const nsID*
+GetIIDArg(uint32_t argc, const JS::Value& val, JSContext* cx)
+{
+ const nsID* iid;
+
+ // If an IID was passed in then use it
+ if (argc) {
+ JSObject* iidobj;
+ if (val.isPrimitive() ||
+ !(iidobj = val.toObjectOrNull()) ||
+ !(iid = xpc_JSObjectToID(cx, iidobj))) {
+ return nullptr;
+ }
+ } else
+ iid = &NS_GET_IID(nsISupports);
+
+ return iid;
+}
+
+NS_IMETHODIMP
+nsJSCID::CreateInstance(HandleValue iidval, JSContext* cx,
+ uint8_t optionalArgc, MutableHandleValue retval)
+{
+ if (!mDetails->IsValid())
+ return NS_ERROR_XPC_BAD_CID;
+
+ if (NS_FAILED(nsXPConnect::SecurityManager()->CanCreateInstance(cx, mDetails->ID()))) {
+ NS_ERROR("how are we not being called from chrome here?");
+ return NS_OK;
+ }
+
+ // If an IID was passed in then use it
+ const nsID* iid = GetIIDArg(optionalArgc, iidval, cx);
+ if (!iid)
+ return NS_ERROR_XPC_BAD_IID;
+
+ nsCOMPtr<nsIComponentManager> compMgr;
+ nsresult rv = NS_GetComponentManager(getter_AddRefs(compMgr));
+ if (NS_FAILED(rv))
+ return NS_ERROR_UNEXPECTED;
+
+ nsCOMPtr<nsISupports> inst;
+ rv = compMgr->CreateInstance(mDetails->ID(), nullptr, *iid, getter_AddRefs(inst));
+ MOZ_ASSERT(NS_FAILED(rv) || inst, "component manager returned success, but instance is null!");
+
+ if (NS_FAILED(rv) || !inst)
+ return NS_ERROR_XPC_CI_RETURNED_FAILURE;
+
+ rv = nsContentUtils::WrapNative(cx, inst, iid, retval);
+ if (NS_FAILED(rv) || retval.isPrimitive())
+ return NS_ERROR_XPC_CANT_CREATE_WN;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsJSCID::GetService(HandleValue iidval, JSContext* cx, uint8_t optionalArgc,
+ MutableHandleValue retval)
+{
+ if (!mDetails->IsValid())
+ return NS_ERROR_XPC_BAD_CID;
+
+ if (NS_FAILED(nsXPConnect::SecurityManager()->CanCreateInstance(cx, mDetails->ID()))) {
+ MOZ_ASSERT(JS_IsExceptionPending(cx),
+ "security manager vetoed GetService without setting exception");
+ return NS_OK;
+ }
+
+ // If an IID was passed in then use it
+ const nsID* iid = GetIIDArg(optionalArgc, iidval, cx);
+ if (!iid)
+ return NS_ERROR_XPC_BAD_IID;
+
+ nsCOMPtr<nsIServiceManager> svcMgr;
+ nsresult rv = NS_GetServiceManager(getter_AddRefs(svcMgr));
+ if (NS_FAILED(rv))
+ return rv;
+
+ nsCOMPtr<nsISupports> srvc;
+ rv = svcMgr->GetService(mDetails->ID(), *iid, getter_AddRefs(srvc));
+ MOZ_ASSERT(NS_FAILED(rv) || srvc, "service manager returned success, but service is null!");
+ if (NS_FAILED(rv) || !srvc)
+ return NS_ERROR_XPC_GS_RETURNED_FAILURE;
+
+ RootedValue v(cx);
+ rv = nsContentUtils::WrapNative(cx, srvc, iid, &v);
+ if (NS_FAILED(rv) || !v.isObject())
+ return NS_ERROR_XPC_CANT_CREATE_WN;
+
+ retval.set(v);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsJSCID::Construct(nsIXPConnectWrappedNative* wrapper,
+ JSContext* cx, JSObject* objArg,
+ const CallArgs& args, bool* _retval)
+{
+ RootedObject obj(cx, objArg);
+ XPCJSContext* xpccx = nsXPConnect::GetContextInstance();
+ if (!xpccx)
+ return NS_ERROR_FAILURE;
+
+ // 'push' a call context and call on it
+ RootedId name(cx, xpccx->GetStringID(XPCJSContext::IDX_CREATE_INSTANCE));
+ XPCCallContext ccx(cx, obj, nullptr, name, args.length(), args.array(),
+ args.rval().address());
+
+ *_retval = XPCWrappedNative::CallMethod(ccx);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsJSCID::HasInstance(nsIXPConnectWrappedNative* wrapper,
+ JSContext* cx, JSObject * /* unused */,
+ HandleValue val, bool* bp, bool* _retval)
+{
+ *bp = false;
+
+ if (!val.isObject())
+ return NS_OK;
+
+ RootedObject obj(cx, &val.toObject());
+
+ // is this really a native xpcom object with a wrapper?
+ RootedObject target(cx);
+ nsresult rv = FindObjectForHasInstance(cx, obj, &target);
+ if (NS_WARN_IF(NS_FAILED(rv)))
+ return rv;
+
+ if (!target || !IS_WN_REFLECTOR(target))
+ return NS_OK;
+
+ if (XPCWrappedNative* other_wrapper = XPCWrappedNative::Get(target)) {
+ if (nsIClassInfo* ci = other_wrapper->GetClassInfo()) {
+ // We consider CID equality to be the thing that matters here.
+ // This is perhaps debatable.
+ nsID cid;
+ if (NS_SUCCEEDED(ci->GetClassIDNoAlloc(&cid)))
+ *bp = cid.Equals(mDetails->ID());
+ }
+ }
+
+ return NS_OK;
+}
+
+/***************************************************************************/
+// additional utilities...
+
+JSObject*
+xpc_NewIDObject(JSContext* cx, HandleObject jsobj, const nsID& aID)
+{
+ RootedObject obj(cx);
+
+ nsCOMPtr<nsIJSID> iid = nsJSID::NewID(aID);
+ if (iid) {
+ nsXPConnect* xpc = nsXPConnect::XPConnect();
+ if (xpc) {
+ xpc->WrapNative(cx, jsobj, static_cast<nsISupports*>(iid),
+ NS_GET_IID(nsIJSID), obj.address());
+ }
+ }
+ return obj;
+}
+
+// note: returned pointer is only valid while |obj| remains alive!
+const nsID*
+xpc_JSObjectToID(JSContext* cx, JSObject* obj)
+{
+ if (!cx || !obj)
+ return nullptr;
+
+ // NOTE: this call does NOT addref
+ XPCWrappedNative* wrapper = nullptr;
+ obj = js::CheckedUnwrap(obj);
+ if (obj && IS_WN_REFLECTOR(obj))
+ wrapper = XPCWrappedNative::Get(obj);
+ if (wrapper &&
+ (wrapper->HasInterfaceNoQI(NS_GET_IID(nsIJSID)) ||
+ wrapper->HasInterfaceNoQI(NS_GET_IID(nsIJSIID)) ||
+ wrapper->HasInterfaceNoQI(NS_GET_IID(nsIJSCID)))) {
+ return ((nsIJSID*)wrapper->GetIdentityObject())->GetID();
+ }
+ return nullptr;
+}
+
+bool
+xpc_JSObjectIsID(JSContext* cx, JSObject* obj)
+{
+ MOZ_ASSERT(cx && obj, "bad param");
+ // NOTE: this call does NOT addref
+ XPCWrappedNative* wrapper = nullptr;
+ obj = js::CheckedUnwrap(obj);
+ if (obj && IS_WN_REFLECTOR(obj))
+ wrapper = XPCWrappedNative::Get(obj);
+ return wrapper &&
+ (wrapper->HasInterfaceNoQI(NS_GET_IID(nsIJSID)) ||
+ wrapper->HasInterfaceNoQI(NS_GET_IID(nsIJSIID)) ||
+ wrapper->HasInterfaceNoQI(NS_GET_IID(nsIJSCID)));
+}
+
+