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: #ifndef nsIClassInfoImpl_h__ michael@0: #define nsIClassInfoImpl_h__ michael@0: michael@0: #include "mozilla/Alignment.h" michael@0: #include "mozilla/Assertions.h" michael@0: #include "mozilla/MacroArgs.h" michael@0: #include "mozilla/MacroForEach.h" michael@0: #include "nsIClassInfo.h" michael@0: #include "nsISupportsImpl.h" michael@0: michael@0: #include michael@0: michael@0: /** michael@0: * This header file provides macros which help you make your class implement michael@0: * nsIClassInfo. Implementing nsIClassInfo is particularly helpful if you have michael@0: * a C++ class which implements multiple interfaces and which you access from michael@0: * JavaScript. If that class implements nsIClassInfo, the JavaScript code michael@0: * won't have to call QueryInterface on instances of the class; all methods michael@0: * from all interfaces returned by GetInterfaces() will be available michael@0: * automagically. michael@0: * michael@0: * Here's all you need to do. Given a class michael@0: * michael@0: * class nsFooBar : public nsIFoo, public nsIBar { }; michael@0: * michael@0: * you should already have the following nsISupports implementation in its cpp michael@0: * file: michael@0: * michael@0: * NS_IMPL_ISUPPORTS(nsFooBar, nsIFoo, nsIBar). michael@0: * michael@0: * Change this to michael@0: * michael@0: * NS_IMPL_CLASSINFO(nsFooBar, nullptr, 0, NS_FOOBAR_CID) michael@0: * NS_IMPL_ISUPPORTS_CI(nsFooBar, nsIFoo, nsIBar) michael@0: * michael@0: * If nsFooBar is threadsafe, change the 0 above to nsIClassInfo::THREADSAFE. michael@0: * If it's a singleton, use nsIClassInfo::SINGLETON. The full list of flags is michael@0: * in nsIClassInfo.idl. michael@0: * michael@0: * The nullptr parameter is there so you can pass a function for converting michael@0: * from an XPCOM object to a scriptable helper. Unless you're doing michael@0: * specialized JS work, you can probably leave this as nullptr. michael@0: * michael@0: * This file also defines the NS_IMPL_QUERY_INTERFACE_CI macro, which you can michael@0: * use to replace NS_IMPL_QUERY_INTERFACE, if you use that instead of michael@0: * NS_IMPL_ISUPPORTS. michael@0: * michael@0: * That's it! The rest is gory details. michael@0: * michael@0: * michael@0: * Notice that nsFooBar didn't need to inherit from nsIClassInfo in order to michael@0: * "implement" it. However, after adding these macros to nsFooBar, you you can michael@0: * QueryInterface an instance of nsFooBar to nsIClassInfo. How can this be? michael@0: * michael@0: * The answer lies in the NS_IMPL_ISUPPORTS_CI macro. It modifies nsFooBar's michael@0: * QueryInterface implementation such that, if we ask to QI to nsIClassInfo, it michael@0: * returns a singleton object associated with the class. (That singleton is michael@0: * defined by NS_IMPL_CLASSINFO.) So all nsFooBar instances will return the michael@0: * same object when QI'ed to nsIClassInfo. (You can see this in michael@0: * NS_IMPL_QUERY_CLASSINFO below.) michael@0: * michael@0: * This hack breaks XPCOM's rules, since if you take an instance of nsFooBar, michael@0: * QI it to nsIClassInfo, and then try to QI to nsIFoo, that will fail. On the michael@0: * upside, implementing nsIClassInfo doesn't add a vtable pointer to instances michael@0: * of your class. michael@0: * michael@0: * In principal, you can also implement nsIClassInfo by inheriting from the michael@0: * interface. But some code expects that when it QI's an object to michael@0: * nsIClassInfo, it gets back a singleton which isn't attached to any michael@0: * particular object. If a class were to implement nsIClassInfo through michael@0: * inheritance, that code might QI to nsIClassInfo and keep the resulting michael@0: * object alive, thinking it was only keeping alive the classinfo singleton, michael@0: * but in fact keeping a whole instance of the class alive. See, e.g., bug michael@0: * 658632. michael@0: * michael@0: * Unless you specifically need to have a different nsIClassInfo instance for michael@0: * each instance of your class, you should probably just implement nsIClassInfo michael@0: * as a singleton. michael@0: */ michael@0: michael@0: class NS_COM_GLUE GenericClassInfo : public nsIClassInfo michael@0: { michael@0: public: michael@0: struct ClassInfoData michael@0: { michael@0: typedef NS_CALLBACK(GetInterfacesProc)(uint32_t* countp, michael@0: nsIID*** array); michael@0: typedef NS_CALLBACK(GetLanguageHelperProc)(uint32_t language, michael@0: nsISupports** helper); michael@0: michael@0: GetInterfacesProc getinterfaces; michael@0: GetLanguageHelperProc getlanguagehelper; michael@0: uint32_t flags; michael@0: nsCID cid; michael@0: }; michael@0: michael@0: NS_DECL_ISUPPORTS_INHERITED michael@0: NS_DECL_NSICLASSINFO michael@0: michael@0: GenericClassInfo(const ClassInfoData* data) michael@0: : mData(data) michael@0: { } michael@0: michael@0: private: michael@0: const ClassInfoData* mData; michael@0: }; michael@0: michael@0: #define NS_CLASSINFO_NAME(_class) g##_class##_classInfoGlobal michael@0: #define NS_CI_INTERFACE_GETTER_NAME(_class) _class##_GetInterfacesHelper michael@0: #define NS_DECL_CI_INTERFACE_GETTER(_class) \ michael@0: extern NS_IMETHODIMP NS_CI_INTERFACE_GETTER_NAME(_class) \ michael@0: (uint32_t *, nsIID ***); michael@0: michael@0: #define NS_IMPL_CLASSINFO(_class, _getlanguagehelper, _flags, _cid) \ michael@0: NS_DECL_CI_INTERFACE_GETTER(_class) \ michael@0: static const GenericClassInfo::ClassInfoData k##_class##ClassInfoData = { \ michael@0: NS_CI_INTERFACE_GETTER_NAME(_class), \ michael@0: _getlanguagehelper, \ michael@0: _flags | nsIClassInfo::SINGLETON_CLASSINFO, \ michael@0: _cid, \ michael@0: }; \ michael@0: mozilla::AlignedStorage2 k##_class##ClassInfoDataPlace; \ michael@0: nsIClassInfo* NS_CLASSINFO_NAME(_class) = nullptr; michael@0: michael@0: #define NS_IMPL_QUERY_CLASSINFO(_class) \ michael@0: if ( aIID.Equals(NS_GET_IID(nsIClassInfo)) ) { \ michael@0: if (!NS_CLASSINFO_NAME(_class)) \ michael@0: NS_CLASSINFO_NAME(_class) = new (k##_class##ClassInfoDataPlace.addr()) \ michael@0: GenericClassInfo(&k##_class##ClassInfoData); \ michael@0: foundInterface = NS_CLASSINFO_NAME(_class); \ michael@0: } else michael@0: michael@0: #define NS_CLASSINFO_HELPER_BEGIN(_class, _c) \ michael@0: NS_IMETHODIMP \ michael@0: NS_CI_INTERFACE_GETTER_NAME(_class)(uint32_t *count, nsIID ***array) \ michael@0: { \ michael@0: *count = _c; \ michael@0: *array = (nsIID **)nsMemory::Alloc(sizeof (nsIID *) * _c); \ michael@0: uint32_t i = 0; michael@0: michael@0: #define NS_CLASSINFO_HELPER_ENTRY(_interface) \ michael@0: (*array)[i++] = (nsIID*)nsMemory::Clone(&NS_GET_IID(_interface), \ michael@0: sizeof(nsIID)); michael@0: michael@0: #define NS_CLASSINFO_HELPER_END \ michael@0: MOZ_ASSERT(i == *count, "Incorrent number of entries"); \ michael@0: return NS_OK; \ michael@0: } michael@0: michael@0: #define NS_IMPL_CI_INTERFACE_GETTER(aClass, ...) \ michael@0: MOZ_STATIC_ASSERT_VALID_ARG_COUNT(__VA_ARGS__); \ michael@0: NS_CLASSINFO_HELPER_BEGIN(aClass, \ michael@0: MOZ_PASTE_PREFIX_AND_ARG_COUNT(/* No prefix */, \ michael@0: __VA_ARGS__)) \ michael@0: MOZ_FOR_EACH(NS_CLASSINFO_HELPER_ENTRY, (), (__VA_ARGS__)) \ michael@0: NS_CLASSINFO_HELPER_END michael@0: michael@0: #define NS_IMPL_QUERY_INTERFACE_CI(aClass, ...) \ michael@0: MOZ_STATIC_ASSERT_VALID_ARG_COUNT(__VA_ARGS__); \ michael@0: NS_INTERFACE_MAP_BEGIN(aClass) \ michael@0: MOZ_FOR_EACH(NS_INTERFACE_MAP_ENTRY, (), (__VA_ARGS__)) \ michael@0: NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, MOZ_ARG_1(__VA_ARGS__)) \ michael@0: NS_IMPL_QUERY_CLASSINFO(aClass) \ michael@0: NS_INTERFACE_MAP_END michael@0: michael@0: #define NS_IMPL_ISUPPORTS_CI(aClass, ...) \ michael@0: NS_IMPL_ADDREF(aClass) \ michael@0: NS_IMPL_RELEASE(aClass) \ michael@0: NS_IMPL_QUERY_INTERFACE_CI(aClass, __VA_ARGS__) \ michael@0: NS_IMPL_CI_INTERFACE_GETTER(aClass, __VA_ARGS__) michael@0: michael@0: #endif // nsIClassInfoImpl_h__