michael@0: /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ michael@0: /* vim: set ts=8 sts=4 et sw=4 tw=99: */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: /* An xpcom implementation of the JavaScript nsIID and nsCID objects. */ michael@0: michael@0: #include "xpcprivate.h" michael@0: #include "mozilla/dom/BindingUtils.h" michael@0: #include "mozilla/Attributes.h" michael@0: #include "JavaScriptParent.h" michael@0: #include "mozilla/StaticPtr.h" michael@0: michael@0: using namespace mozilla::dom; michael@0: using namespace JS; michael@0: michael@0: /***************************************************************************/ michael@0: // nsJSID michael@0: michael@0: NS_IMPL_CLASSINFO(nsJSID, nullptr, 0, NS_JS_ID_CID) michael@0: NS_IMPL_ISUPPORTS_CI(nsJSID, nsIJSID) michael@0: michael@0: char nsJSID::gNoString[] = ""; michael@0: michael@0: nsJSID::nsJSID() michael@0: : mID(GetInvalidIID()), mNumber(gNoString), mName(gNoString) michael@0: { michael@0: } michael@0: michael@0: nsJSID::~nsJSID() michael@0: { michael@0: if (mNumber && mNumber != gNoString) michael@0: NS_Free(mNumber); michael@0: if (mName && mName != gNoString) michael@0: NS_Free(mName); michael@0: } michael@0: michael@0: void nsJSID::Reset() michael@0: { michael@0: mID = GetInvalidIID(); michael@0: michael@0: if (mNumber && mNumber != gNoString) michael@0: NS_Free(mNumber); michael@0: if (mName && mName != gNoString) michael@0: NS_Free(mName); michael@0: michael@0: mNumber = mName = nullptr; michael@0: } michael@0: michael@0: bool michael@0: nsJSID::SetName(const char* name) michael@0: { michael@0: MOZ_ASSERT(!mName || mName == gNoString ,"name already set"); michael@0: MOZ_ASSERT(name,"null name"); michael@0: mName = NS_strdup(name); michael@0: return mName ? true : false; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsJSID::GetName(char * *aName) michael@0: { michael@0: if (!aName) michael@0: return NS_ERROR_NULL_POINTER; michael@0: michael@0: if (!NameIsSet()) michael@0: SetNameToNoString(); michael@0: MOZ_ASSERT(mName, "name not set"); michael@0: *aName = NS_strdup(mName); michael@0: return *aName ? NS_OK : NS_ERROR_OUT_OF_MEMORY; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsJSID::GetNumber(char * *aNumber) michael@0: { michael@0: if (!aNumber) michael@0: return NS_ERROR_NULL_POINTER; michael@0: michael@0: if (!mNumber) { michael@0: if (!(mNumber = mID.ToString())) michael@0: mNumber = gNoString; michael@0: } michael@0: michael@0: *aNumber = NS_strdup(mNumber); michael@0: return *aNumber ? NS_OK : NS_ERROR_OUT_OF_MEMORY; michael@0: } michael@0: michael@0: NS_IMETHODIMP_(const nsID*) michael@0: nsJSID::GetID() michael@0: { michael@0: return &mID; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsJSID::GetValid(bool *aValid) michael@0: { michael@0: if (!aValid) michael@0: return NS_ERROR_NULL_POINTER; michael@0: michael@0: *aValid = IsValid(); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsJSID::Equals(nsIJSID *other, bool *_retval) michael@0: { michael@0: if (!_retval) michael@0: return NS_ERROR_NULL_POINTER; michael@0: michael@0: if (!other || mID.Equals(GetInvalidIID())) { michael@0: *_retval = false; michael@0: return NS_OK; michael@0: } michael@0: michael@0: *_retval = other->GetID()->Equals(mID); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsJSID::Initialize(const char *idString) michael@0: { michael@0: if (!idString) michael@0: return NS_ERROR_NULL_POINTER; michael@0: michael@0: if (*idString != '\0' && mID.Equals(GetInvalidIID())) { michael@0: Reset(); michael@0: michael@0: if (idString[0] == '{') { michael@0: if (mID.Parse(idString)) { michael@0: return NS_OK; michael@0: } michael@0: michael@0: // error - reset to invalid state michael@0: mID = GetInvalidIID(); michael@0: } michael@0: } michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: bool michael@0: nsJSID::InitWithName(const nsID& id, const char *nameString) michael@0: { michael@0: MOZ_ASSERT(nameString, "no name"); michael@0: Reset(); michael@0: mID = id; michael@0: return SetName(nameString); michael@0: } michael@0: michael@0: // try to use the name, if no name, then use the number michael@0: NS_IMETHODIMP michael@0: nsJSID::ToString(char **_retval) michael@0: { michael@0: if (mName && mName != gNoString) michael@0: return GetName(_retval); michael@0: michael@0: return GetNumber(_retval); michael@0: } michael@0: michael@0: const nsID& michael@0: nsJSID::GetInvalidIID() const michael@0: { michael@0: // {BB1F47B0-D137-11d2-9841-006008962422} michael@0: static const nsID invalid = {0xbb1f47b0, 0xd137, 0x11d2, michael@0: {0x98, 0x41, 0x0, 0x60, 0x8, 0x96, 0x24, 0x22}}; michael@0: return invalid; michael@0: } michael@0: michael@0: //static michael@0: already_AddRefed michael@0: nsJSID::NewID(const char* str) michael@0: { michael@0: if (!str) { michael@0: NS_ERROR("no string"); michael@0: return nullptr; michael@0: } michael@0: michael@0: nsRefPtr idObj = new nsJSID(); michael@0: NS_ENSURE_SUCCESS(idObj->Initialize(str), nullptr); michael@0: return idObj.forget(); michael@0: } michael@0: michael@0: //static michael@0: already_AddRefed michael@0: nsJSID::NewID(const nsID& id) michael@0: { michael@0: nsRefPtr idObj = new nsJSID(); michael@0: idObj->mID = id; michael@0: idObj->mName = nullptr; michael@0: idObj->mNumber = nullptr; michael@0: return idObj.forget(); michael@0: } michael@0: michael@0: michael@0: /***************************************************************************/ michael@0: // Class object support so that we can share prototypes of wrapper michael@0: michael@0: // This class exists just so we can have a shared scriptable helper for michael@0: // the nsJSIID class. The instances implement their own helpers. But we michael@0: // needed to be able to indicate to the shared prototypes this single flag: michael@0: // nsIXPCScriptable::DONT_ENUM_STATIC_PROPS. And having a class to do it is michael@0: // the only means we have. Setting this flag on any given instance scriptable michael@0: // helper is not sufficient to convey the information that we don't want michael@0: // static properties enumerated on the shared proto. michael@0: michael@0: class SharedScriptableHelperForJSIID MOZ_FINAL : public nsIXPCScriptable michael@0: { michael@0: public: michael@0: NS_DECL_ISUPPORTS michael@0: NS_DECL_NSIXPCSCRIPTABLE michael@0: SharedScriptableHelperForJSIID() {} michael@0: }; michael@0: michael@0: NS_INTERFACE_MAP_BEGIN(SharedScriptableHelperForJSIID) michael@0: NS_INTERFACE_MAP_ENTRY(nsIXPCScriptable) michael@0: NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIXPCScriptable) michael@0: NS_INTERFACE_MAP_END michael@0: michael@0: NS_IMPL_ADDREF(SharedScriptableHelperForJSIID) michael@0: NS_IMPL_RELEASE(SharedScriptableHelperForJSIID) michael@0: michael@0: // The nsIXPCScriptable map declaration that will generate stubs for us... michael@0: #define XPC_MAP_CLASSNAME SharedScriptableHelperForJSIID michael@0: #define XPC_MAP_QUOTED_CLASSNAME "JSIID" michael@0: #define XPC_MAP_FLAGS nsIXPCScriptable::DONT_ENUM_STATIC_PROPS |\ michael@0: nsIXPCScriptable::ALLOW_PROP_MODS_DURING_RESOLVE michael@0: #include "xpc_map_end.h" /* This will #undef the above */ michael@0: michael@0: static mozilla::StaticRefPtr gSharedScriptableHelperForJSIID; michael@0: static bool gClassObjectsWereInited = false; michael@0: michael@0: static void EnsureClassObjectsInitialized() michael@0: { michael@0: if (!gClassObjectsWereInited) { michael@0: gSharedScriptableHelperForJSIID = new SharedScriptableHelperForJSIID(); michael@0: michael@0: gClassObjectsWereInited = true; michael@0: } michael@0: } michael@0: michael@0: NS_METHOD GetSharedScriptableHelperForJSIID(uint32_t language, michael@0: nsISupports **helper) michael@0: { michael@0: EnsureClassObjectsInitialized(); michael@0: if (language == nsIProgrammingLanguage::JAVASCRIPT) { michael@0: nsCOMPtr temp = gSharedScriptableHelperForJSIID.get(); michael@0: temp.forget(helper); michael@0: } else michael@0: *helper = nullptr; michael@0: return NS_OK; michael@0: } michael@0: michael@0: /******************************************************/ michael@0: michael@0: #define NULL_CID \ michael@0: { 0x00000000, 0x0000, 0x0000, \ michael@0: { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } } michael@0: michael@0: // We pass nsIClassInfo::DOM_OBJECT so that nsJSIID instances may be created michael@0: // in unprivileged scopes. michael@0: NS_DECL_CI_INTERFACE_GETTER(nsJSIID) michael@0: NS_IMPL_CLASSINFO(nsJSIID, GetSharedScriptableHelperForJSIID, michael@0: nsIClassInfo::DOM_OBJECT, NULL_CID) michael@0: michael@0: NS_DECL_CI_INTERFACE_GETTER(nsJSCID) michael@0: NS_IMPL_CLASSINFO(nsJSCID, nullptr, 0, NULL_CID) michael@0: michael@0: void xpc_DestroyJSxIDClassObjects() michael@0: { michael@0: if (gClassObjectsWereInited) { michael@0: NS_IF_RELEASE(NS_CLASSINFO_NAME(nsJSIID)); michael@0: NS_IF_RELEASE(NS_CLASSINFO_NAME(nsJSCID)); michael@0: gSharedScriptableHelperForJSIID = nullptr; michael@0: michael@0: gClassObjectsWereInited = false; michael@0: } michael@0: } michael@0: michael@0: /***************************************************************************/ michael@0: michael@0: NS_INTERFACE_MAP_BEGIN(nsJSIID) michael@0: NS_INTERFACE_MAP_ENTRY(nsIJSID) michael@0: NS_INTERFACE_MAP_ENTRY(nsIJSIID) michael@0: NS_INTERFACE_MAP_ENTRY(nsIXPCScriptable) michael@0: NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIJSID) michael@0: NS_IMPL_QUERY_CLASSINFO(nsJSIID) michael@0: NS_INTERFACE_MAP_END michael@0: michael@0: NS_IMPL_ADDREF(nsJSIID) michael@0: NS_IMPL_RELEASE(nsJSIID) michael@0: NS_IMPL_CI_INTERFACE_GETTER(nsJSIID, nsIJSID, nsIJSIID) michael@0: michael@0: // The nsIXPCScriptable map declaration that will generate stubs for us... michael@0: #define XPC_MAP_CLASSNAME nsJSIID michael@0: #define XPC_MAP_QUOTED_CLASSNAME "nsJSIID" michael@0: #define XPC_MAP_WANT_NEWRESOLVE michael@0: #define XPC_MAP_WANT_ENUMERATE michael@0: #define XPC_MAP_WANT_HASINSTANCE michael@0: #define XPC_MAP_FLAGS nsIXPCScriptable::DONT_ENUM_STATIC_PROPS |\ michael@0: nsIXPCScriptable::ALLOW_PROP_MODS_DURING_RESOLVE michael@0: #include "xpc_map_end.h" /* This will #undef the above */ michael@0: michael@0: michael@0: nsJSIID::nsJSIID(nsIInterfaceInfo* aInfo) michael@0: : mInfo(aInfo) michael@0: { michael@0: } michael@0: michael@0: nsJSIID::~nsJSIID() {} michael@0: michael@0: // If mInfo is present we use it and ignore mDetails, else we use mDetails. michael@0: michael@0: NS_IMETHODIMP nsJSIID::GetName(char * *aName) michael@0: { michael@0: return mInfo->GetName(aName); michael@0: } michael@0: michael@0: NS_IMETHODIMP nsJSIID::GetNumber(char * *aNumber) michael@0: { michael@0: char str[NSID_LENGTH]; michael@0: const nsIID* id; michael@0: mInfo->GetIIDShared(&id); michael@0: id->ToProvidedString(str); michael@0: *aNumber = (char*) nsMemory::Clone(str, NSID_LENGTH); michael@0: return *aNumber ? NS_OK : NS_ERROR_OUT_OF_MEMORY; michael@0: } michael@0: michael@0: NS_IMETHODIMP_(const nsID*) nsJSIID::GetID() michael@0: { michael@0: const nsIID* id; michael@0: mInfo->GetIIDShared(&id); michael@0: return id; michael@0: } michael@0: michael@0: NS_IMETHODIMP nsJSIID::GetValid(bool *aValid) michael@0: { michael@0: *aValid = true; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP nsJSIID::Equals(nsIJSID *other, bool *_retval) michael@0: { michael@0: if (!_retval) michael@0: return NS_ERROR_NULL_POINTER; michael@0: michael@0: if (!other) { michael@0: *_retval = false; michael@0: return NS_OK; michael@0: } michael@0: michael@0: mInfo->IsIID(other->GetID(), _retval); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP nsJSIID::Initialize(const char *idString) michael@0: { michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: NS_IMETHODIMP nsJSIID::ToString(char **_retval) michael@0: { michael@0: return mInfo->GetName(_retval); michael@0: } michael@0: michael@0: // static michael@0: already_AddRefed michael@0: nsJSIID::NewID(nsIInterfaceInfo* aInfo) michael@0: { michael@0: if (!aInfo) { michael@0: NS_ERROR("no info"); michael@0: return nullptr; michael@0: } michael@0: michael@0: bool canScript; michael@0: if (NS_FAILED(aInfo->IsScriptable(&canScript)) || !canScript) michael@0: return nullptr; michael@0: michael@0: nsRefPtr idObj = new nsJSIID(aInfo); michael@0: return idObj.forget(); michael@0: } michael@0: michael@0: michael@0: /* bool resolve (in nsIXPConnectWrappedNative wrapper, in JSContextPtr cx, in JSObjectPtr obj, in jsval id); */ michael@0: NS_IMETHODIMP michael@0: nsJSIID::NewResolve(nsIXPConnectWrappedNative *wrapper, michael@0: JSContext * cx, JSObject * objArg, michael@0: jsid idArg, JSObject * *objp, michael@0: bool *_retval) michael@0: { michael@0: RootedObject obj(cx, objArg); michael@0: RootedId id(cx, idArg); michael@0: XPCCallContext ccx(JS_CALLER, cx); michael@0: michael@0: AutoMarkingNativeInterfacePtr iface(ccx); michael@0: michael@0: const nsIID* iid; michael@0: mInfo->GetIIDShared(&iid); michael@0: michael@0: iface = XPCNativeInterface::GetNewOrUsed(iid); michael@0: michael@0: if (!iface) michael@0: return NS_OK; michael@0: michael@0: XPCNativeMember* member = iface->FindMember(id); michael@0: if (member && member->IsConstant()) { michael@0: RootedValue val(cx); michael@0: if (!member->GetConstantValue(ccx, iface, val.address())) michael@0: return NS_ERROR_OUT_OF_MEMORY; michael@0: michael@0: *objp = obj; michael@0: *_retval = JS_DefinePropertyById(cx, obj, id, val, nullptr, nullptr, michael@0: JSPROP_ENUMERATE | JSPROP_READONLY | michael@0: JSPROP_PERMANENT); michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: /* bool enumerate (in nsIXPConnectWrappedNative wrapper, in JSContextPtr cx, in JSObjectPtr obj); */ michael@0: NS_IMETHODIMP michael@0: nsJSIID::Enumerate(nsIXPConnectWrappedNative *wrapper, michael@0: JSContext * cx, JSObject * objArg, bool *_retval) michael@0: { michael@0: // In this case, let's just eagerly resolve... michael@0: michael@0: RootedObject obj(cx, objArg); michael@0: XPCCallContext ccx(JS_CALLER, cx); michael@0: michael@0: AutoMarkingNativeInterfacePtr iface(ccx); michael@0: michael@0: const nsIID* iid; michael@0: mInfo->GetIIDShared(&iid); michael@0: michael@0: iface = XPCNativeInterface::GetNewOrUsed(iid); michael@0: michael@0: if (!iface) michael@0: return NS_OK; michael@0: michael@0: uint16_t count = iface->GetMemberCount(); michael@0: for (uint16_t i = 0; i < count; i++) { michael@0: XPCNativeMember* member = iface->GetMemberAt(i); michael@0: if (member && member->IsConstant() && michael@0: !xpc_ForcePropertyResolve(cx, obj, member->GetName())) { michael@0: return NS_ERROR_UNEXPECTED; michael@0: } michael@0: } michael@0: return NS_OK; michael@0: } michael@0: michael@0: /* michael@0: * HasInstance hooks need to find an appropriate reflector in order to function michael@0: * properly. There are two complexities that we need to handle: michael@0: * michael@0: * 1 - Cross-compartment wrappers. Chrome uses over 100 compartments, all with michael@0: * system principal. The success of an instanceof check should not depend michael@0: * on which compartment an object comes from. At the same time, we want to michael@0: * make sure we don't unwrap important security wrappers. michael@0: * CheckedUnwrap does the right thing here. michael@0: * michael@0: * 2 - Prototype chains. Suppose someone creates a vanilla JS object |a| and michael@0: * sets its __proto__ to some WN |b|. If |b instanceof nsIFoo| returns true, michael@0: * one would expect |a instanceof nsIFoo| to return true as well, since michael@0: * instanceof is transitive up the prototype chain in ECMAScript. Moreover, michael@0: * there's chrome code that relies on this. michael@0: * michael@0: * This static method handles both complexities, returning either an XPCWN, a michael@0: * DOM object, or null. The object may well be cross-compartment from |cx|. michael@0: */ michael@0: static JSObject * michael@0: FindObjectForHasInstance(JSContext *cx, HandleObject objArg) michael@0: { michael@0: RootedObject obj(cx, objArg), proto(cx); michael@0: michael@0: while (obj && !IS_WN_REFLECTOR(obj) && michael@0: !IsDOMObject(obj) && !mozilla::jsipc::JavaScriptParent::IsCPOW(obj)) michael@0: { michael@0: if (js::IsWrapper(obj)) { michael@0: obj = js::CheckedUnwrap(obj, /* stopAtOuter = */ false); michael@0: continue; michael@0: } michael@0: if (!js::GetObjectProto(cx, obj, &proto)) michael@0: return nullptr; michael@0: obj = proto; michael@0: } michael@0: return obj; michael@0: } michael@0: michael@0: nsresult michael@0: xpc::HasInstance(JSContext *cx, HandleObject objArg, const nsID *iid, bool *bp) michael@0: { michael@0: *bp = false; michael@0: michael@0: RootedObject obj(cx, FindObjectForHasInstance(cx, objArg)); michael@0: if (!obj) michael@0: return NS_OK; michael@0: michael@0: if (IsDOMObject(obj)) { michael@0: // Not all DOM objects implement nsISupports. But if they don't, michael@0: // there's nothing to do in this HasInstance hook. michael@0: nsISupports *identity = UnwrapDOMObjectToISupports(obj); michael@0: if (!identity) michael@0: return NS_OK;; michael@0: nsCOMPtr supp; michael@0: identity->QueryInterface(*iid, getter_AddRefs(supp)); michael@0: *bp = supp; michael@0: return NS_OK; michael@0: } michael@0: michael@0: if (mozilla::jsipc::JavaScriptParent::IsCPOW(obj)) michael@0: return mozilla::jsipc::JavaScriptParent::InstanceOf(obj, iid, bp); michael@0: michael@0: MOZ_ASSERT(IS_WN_REFLECTOR(obj)); michael@0: XPCWrappedNative* other_wrapper = XPCWrappedNative::Get(obj); michael@0: if (!other_wrapper) michael@0: return NS_OK; michael@0: michael@0: // We'll trust the interface set of the wrapper if this is known michael@0: // to be an interface that the objects *expects* to be able to michael@0: // handle. michael@0: if (other_wrapper->HasInterfaceNoQI(*iid)) { michael@0: *bp = true; michael@0: return NS_OK; michael@0: } michael@0: michael@0: // Otherwise, we'll end up Querying the native object to be sure. michael@0: XPCCallContext ccx(JS_CALLER, cx); michael@0: michael@0: AutoMarkingNativeInterfacePtr iface(ccx); michael@0: iface = XPCNativeInterface::GetNewOrUsed(iid); michael@0: michael@0: nsresult findResult = NS_OK; michael@0: if (iface && other_wrapper->FindTearOff(iface, false, &findResult)) michael@0: *bp = true; michael@0: if (NS_FAILED(findResult) && findResult != NS_ERROR_NO_INTERFACE) michael@0: return findResult; michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: /* bool hasInstance (in nsIXPConnectWrappedNative wrapper, in JSContextPtr cx, in JSObjectPtr obj, in jsval val, out bool bp); */ michael@0: NS_IMETHODIMP michael@0: nsJSIID::HasInstance(nsIXPConnectWrappedNative *wrapper, michael@0: JSContext *cx, JSObject * /* unused */, michael@0: HandleValue val, bool *bp, bool *_retval) michael@0: { michael@0: *bp = false; michael@0: michael@0: if (val.isPrimitive()) michael@0: return NS_OK; michael@0: michael@0: // we have a JSObject michael@0: RootedObject obj(cx, &val.toObject()); michael@0: michael@0: const nsIID* iid; michael@0: mInfo->GetIIDShared(&iid); michael@0: return xpc::HasInstance(cx, obj, iid, bp); michael@0: } michael@0: michael@0: /***************************************************************************/ michael@0: michael@0: NS_INTERFACE_MAP_BEGIN(nsJSCID) michael@0: NS_INTERFACE_MAP_ENTRY(nsIJSID) michael@0: NS_INTERFACE_MAP_ENTRY(nsIJSCID) michael@0: NS_INTERFACE_MAP_ENTRY(nsIXPCScriptable) michael@0: NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIJSID) michael@0: NS_IMPL_QUERY_CLASSINFO(nsJSCID) michael@0: NS_INTERFACE_MAP_END michael@0: michael@0: NS_IMPL_ADDREF(nsJSCID) michael@0: NS_IMPL_RELEASE(nsJSCID) michael@0: NS_IMPL_CI_INTERFACE_GETTER(nsJSCID, nsIJSID, nsIJSCID) michael@0: michael@0: // The nsIXPCScriptable map declaration that will generate stubs for us... michael@0: #define XPC_MAP_CLASSNAME nsJSCID michael@0: #define XPC_MAP_QUOTED_CLASSNAME "nsJSCID" michael@0: #define XPC_MAP_WANT_CONSTRUCT michael@0: #define XPC_MAP_WANT_HASINSTANCE michael@0: #define XPC_MAP_FLAGS 0 michael@0: #include "xpc_map_end.h" /* This will #undef the above */ michael@0: michael@0: nsJSCID::nsJSCID() {} michael@0: nsJSCID::~nsJSCID() {} michael@0: michael@0: NS_IMETHODIMP nsJSCID::GetName(char * *aName) michael@0: {ResolveName(); return mDetails.GetName(aName);} michael@0: michael@0: NS_IMETHODIMP nsJSCID::GetNumber(char * *aNumber) michael@0: {return mDetails.GetNumber(aNumber);} michael@0: michael@0: NS_IMETHODIMP_(const nsID*) nsJSCID::GetID() michael@0: {return &mDetails.ID();} michael@0: michael@0: NS_IMETHODIMP nsJSCID::GetValid(bool *aValid) michael@0: {return mDetails.GetValid(aValid);} michael@0: michael@0: NS_IMETHODIMP nsJSCID::Equals(nsIJSID *other, bool *_retval) michael@0: {return mDetails.Equals(other, _retval);} michael@0: michael@0: NS_IMETHODIMP nsJSCID::Initialize(const char *idString) michael@0: {return mDetails.Initialize(idString);} michael@0: michael@0: NS_IMETHODIMP nsJSCID::ToString(char **_retval) michael@0: {ResolveName(); return mDetails.ToString(_retval);} michael@0: michael@0: void michael@0: nsJSCID::ResolveName() michael@0: { michael@0: if (!mDetails.NameIsSet()) michael@0: mDetails.SetNameToNoString(); michael@0: } michael@0: michael@0: //static michael@0: already_AddRefed michael@0: nsJSCID::NewID(const char* str) michael@0: { michael@0: if (!str) { michael@0: NS_ERROR("no string"); michael@0: return nullptr; michael@0: } michael@0: michael@0: nsRefPtr idObj = new nsJSCID(); michael@0: if (str[0] == '{') { michael@0: NS_ENSURE_SUCCESS(idObj->Initialize(str), nullptr); michael@0: } else { michael@0: nsCOMPtr registrar; michael@0: NS_GetComponentRegistrar(getter_AddRefs(registrar)); michael@0: NS_ENSURE_TRUE(registrar, nullptr); michael@0: michael@0: nsCID *cid; michael@0: if (NS_FAILED(registrar->ContractIDToCID(str, &cid))) michael@0: return nullptr; michael@0: bool success = idObj->mDetails.InitWithName(*cid, str); michael@0: nsMemory::Free(cid); michael@0: if (!success) michael@0: return nullptr; michael@0: } michael@0: return idObj.forget(); michael@0: } michael@0: michael@0: static const nsID* michael@0: GetIIDArg(uint32_t argc, const JS::Value& val, JSContext* cx) michael@0: { michael@0: const nsID* iid; michael@0: michael@0: // If an IID was passed in then use it michael@0: if (argc) { michael@0: JSObject* iidobj; michael@0: if (JSVAL_IS_PRIMITIVE(val) || michael@0: !(iidobj = JSVAL_TO_OBJECT(val)) || michael@0: !(iid = xpc_JSObjectToID(cx, iidobj))) { michael@0: return nullptr; michael@0: } michael@0: } else michael@0: iid = &NS_GET_IID(nsISupports); michael@0: michael@0: return iid; michael@0: } michael@0: michael@0: static void michael@0: GetWrapperObject(MutableHandleObject obj) michael@0: { michael@0: obj.set(nullptr); michael@0: nsXPConnect* xpc = nsXPConnect::XPConnect(); michael@0: nsAXPCNativeCallContext *ccxp = nullptr; michael@0: xpc->GetCurrentNativeCallContext(&ccxp); michael@0: if (!ccxp) michael@0: return; michael@0: michael@0: nsCOMPtr wrapper; michael@0: ccxp->GetCalleeWrapper(getter_AddRefs(wrapper)); michael@0: obj.set(wrapper->GetJSObject()); michael@0: } michael@0: michael@0: /* nsISupports createInstance (); */ michael@0: NS_IMETHODIMP michael@0: nsJSCID::CreateInstance(HandleValue iidval, JSContext *cx, michael@0: uint8_t optionalArgc, MutableHandleValue retval) michael@0: { michael@0: if (!mDetails.IsValid()) michael@0: return NS_ERROR_XPC_BAD_CID; michael@0: michael@0: RootedObject obj(cx); michael@0: GetWrapperObject(&obj); michael@0: if (!obj) { michael@0: return NS_ERROR_UNEXPECTED; michael@0: } michael@0: michael@0: nsIXPCSecurityManager* sm = nsXPConnect::XPConnect()->GetDefaultSecurityManager(); michael@0: if (sm && NS_FAILED(sm->CanCreateInstance(cx, mDetails.ID()))) { michael@0: NS_ERROR("how are we not being called from chrome here?"); michael@0: return NS_OK; michael@0: } michael@0: michael@0: // If an IID was passed in then use it michael@0: const nsID* iid = GetIIDArg(optionalArgc, iidval, cx); michael@0: if (!iid) michael@0: return NS_ERROR_XPC_BAD_IID; michael@0: michael@0: nsCOMPtr compMgr; michael@0: nsresult rv = NS_GetComponentManager(getter_AddRefs(compMgr)); michael@0: if (NS_FAILED(rv)) michael@0: return NS_ERROR_UNEXPECTED; michael@0: michael@0: nsCOMPtr inst; michael@0: rv = compMgr->CreateInstance(mDetails.ID(), nullptr, *iid, getter_AddRefs(inst)); michael@0: MOZ_ASSERT(NS_FAILED(rv) || inst, "component manager returned success, but instance is null!"); michael@0: michael@0: if (NS_FAILED(rv) || !inst) michael@0: return NS_ERROR_XPC_CI_RETURNED_FAILURE; michael@0: michael@0: rv = nsXPConnect::XPConnect()->WrapNativeToJSVal(cx, obj, inst, nullptr, iid, true, retval); michael@0: if (NS_FAILED(rv) || retval.isPrimitive()) michael@0: return NS_ERROR_XPC_CANT_CREATE_WN; michael@0: return NS_OK; michael@0: } michael@0: michael@0: /* nsISupports getService (); */ michael@0: NS_IMETHODIMP michael@0: nsJSCID::GetService(HandleValue iidval, JSContext *cx, uint8_t optionalArgc, michael@0: MutableHandleValue retval) michael@0: { michael@0: if (!mDetails.IsValid()) michael@0: return NS_ERROR_XPC_BAD_CID; michael@0: michael@0: RootedObject obj(cx); michael@0: GetWrapperObject(&obj); michael@0: if (!obj) { michael@0: return NS_ERROR_UNEXPECTED; michael@0: } michael@0: michael@0: nsIXPCSecurityManager *sm; michael@0: sm = nsXPConnect::XPConnect()->GetDefaultSecurityManager(); michael@0: if (sm && NS_FAILED(sm->CanCreateInstance(cx, mDetails.ID()))) { michael@0: MOZ_ASSERT(JS_IsExceptionPending(cx), michael@0: "security manager vetoed GetService without setting exception"); michael@0: return NS_OK; michael@0: } michael@0: michael@0: // If an IID was passed in then use it michael@0: const nsID *iid = GetIIDArg(optionalArgc, iidval, cx); michael@0: if (!iid) michael@0: return NS_ERROR_XPC_BAD_IID; michael@0: michael@0: nsCOMPtr svcMgr; michael@0: nsresult rv = NS_GetServiceManager(getter_AddRefs(svcMgr)); michael@0: if (NS_FAILED(rv)) michael@0: return rv; michael@0: michael@0: nsCOMPtr srvc; michael@0: rv = svcMgr->GetService(mDetails.ID(), *iid, getter_AddRefs(srvc)); michael@0: MOZ_ASSERT(NS_FAILED(rv) || srvc, "service manager returned success, but service is null!"); michael@0: if (NS_FAILED(rv) || !srvc) michael@0: return NS_ERROR_XPC_GS_RETURNED_FAILURE; michael@0: michael@0: nsCOMPtr holder; michael@0: rv = nsXPConnect::XPConnect()->WrapNative(cx, obj, srvc, *iid, getter_AddRefs(holder)); michael@0: if (NS_FAILED(rv) || !holder || !holder->GetJSObject()) michael@0: return NS_ERROR_XPC_CANT_CREATE_WN; michael@0: michael@0: retval.setObject(*holder->GetJSObject()); michael@0: return NS_OK; michael@0: } michael@0: michael@0: /* bool construct (in nsIXPConnectWrappedNative wrapper, in JSContextPtr cx, in JSObjectPtr obj, in uint32_t argc, in JSValPtr argv, in JSValPtr vp); */ michael@0: NS_IMETHODIMP michael@0: nsJSCID::Construct(nsIXPConnectWrappedNative *wrapper, michael@0: JSContext *cx, JSObject *objArg, michael@0: const CallArgs &args, bool *_retval) michael@0: { michael@0: RootedObject obj(cx, objArg); michael@0: XPCJSRuntime* rt = nsXPConnect::GetRuntimeInstance(); michael@0: if (!rt) michael@0: return NS_ERROR_FAILURE; michael@0: michael@0: // 'push' a call context and call on it michael@0: RootedId name(cx, rt->GetStringID(XPCJSRuntime::IDX_CREATE_INSTANCE)); michael@0: XPCCallContext ccx(JS_CALLER, cx, obj, JS::NullPtr(), name, args.length(), args.array(), michael@0: args.rval().address()); michael@0: michael@0: *_retval = XPCWrappedNative::CallMethod(ccx); michael@0: return NS_OK; michael@0: } michael@0: michael@0: /* bool hasInstance (in nsIXPConnectWrappedNative wrapper, in JSContextPtr cx, in JSObjectPtr obj, in jsval val, out bool bp); */ michael@0: NS_IMETHODIMP michael@0: nsJSCID::HasInstance(nsIXPConnectWrappedNative *wrapper, michael@0: JSContext *cx, JSObject * /* unused */, michael@0: HandleValue val, bool *bp, bool *_retval) michael@0: { michael@0: *bp = false; michael@0: nsresult rv = NS_OK; michael@0: michael@0: if (val.isObject()) { michael@0: // we have a JSObject michael@0: RootedObject obj(cx, &val.toObject()); michael@0: michael@0: MOZ_ASSERT(obj, "when is an object not an object?"); michael@0: michael@0: // is this really a native xpcom object with a wrapper? michael@0: nsIClassInfo *ci = nullptr; michael@0: obj = FindObjectForHasInstance(cx, obj); michael@0: if (!obj || !IS_WN_REFLECTOR(obj)) michael@0: return rv; michael@0: if (XPCWrappedNative *other_wrapper = XPCWrappedNative::Get(obj)) michael@0: ci = other_wrapper->GetClassInfo(); michael@0: michael@0: // We consider CID equality to be the thing that matters here. michael@0: // This is perhaps debatable. michael@0: if (ci) { michael@0: nsID cid; michael@0: if (NS_SUCCEEDED(ci->GetClassIDNoAlloc(&cid))) michael@0: *bp = cid.Equals(mDetails.ID()); michael@0: } michael@0: } michael@0: michael@0: return rv; michael@0: } michael@0: michael@0: /***************************************************************************/ michael@0: // additional utilities... michael@0: michael@0: JSObject * michael@0: xpc_NewIDObject(JSContext *cx, HandleObject jsobj, const nsID& aID) michael@0: { michael@0: RootedObject obj(cx); michael@0: michael@0: nsCOMPtr iid = nsJSID::NewID(aID); michael@0: if (iid) { michael@0: nsXPConnect *xpc = nsXPConnect::XPConnect(); michael@0: if (xpc) { michael@0: nsCOMPtr holder; michael@0: nsresult rv = xpc->WrapNative(cx, jsobj, michael@0: static_cast(iid), michael@0: NS_GET_IID(nsIJSID), michael@0: getter_AddRefs(holder)); michael@0: if (NS_SUCCEEDED(rv) && holder) { michael@0: obj = holder->GetJSObject(); michael@0: } michael@0: } michael@0: } michael@0: return obj; michael@0: } michael@0: michael@0: // note: returned pointer is only valid while |obj| remains alive! michael@0: const nsID* michael@0: xpc_JSObjectToID(JSContext *cx, JSObject *obj) michael@0: { michael@0: if (!cx || !obj) michael@0: return nullptr; michael@0: michael@0: // NOTE: this call does NOT addref michael@0: XPCWrappedNative *wrapper = nullptr; michael@0: obj = js::CheckedUnwrap(obj); michael@0: if (obj && IS_WN_REFLECTOR(obj)) michael@0: wrapper = XPCWrappedNative::Get(obj); michael@0: if (wrapper && michael@0: (wrapper->HasInterfaceNoQI(NS_GET_IID(nsIJSID)) || michael@0: wrapper->HasInterfaceNoQI(NS_GET_IID(nsIJSIID)) || michael@0: wrapper->HasInterfaceNoQI(NS_GET_IID(nsIJSCID)))) { michael@0: return ((nsIJSID*)wrapper->GetIdentityObject())->GetID(); michael@0: } michael@0: return nullptr; michael@0: } michael@0: michael@0: bool michael@0: xpc_JSObjectIsID(JSContext *cx, JSObject *obj) michael@0: { michael@0: MOZ_ASSERT(cx && obj, "bad param"); michael@0: // NOTE: this call does NOT addref michael@0: XPCWrappedNative *wrapper = nullptr; michael@0: obj = js::CheckedUnwrap(obj); michael@0: if (obj && IS_WN_REFLECTOR(obj)) michael@0: wrapper = XPCWrappedNative::Get(obj); michael@0: return wrapper && michael@0: (wrapper->HasInterfaceNoQI(NS_GET_IID(nsIJSID)) || michael@0: wrapper->HasInterfaceNoQI(NS_GET_IID(nsIJSIID)) || michael@0: wrapper->HasInterfaceNoQI(NS_GET_IID(nsIJSCID))); michael@0: } michael@0: michael@0: