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 #ifndef nsIClassInfoImpl_h__ 8 #define nsIClassInfoImpl_h__ 9 10 #include "mozilla/Alignment.h" 11 #include "mozilla/Assertions.h" 12 #include "mozilla/MacroArgs.h" 13 #include "mozilla/MacroForEach.h" 14 #include "nsIClassInfo.h" 15 #include "nsISupportsImpl.h" 16 17 #include <new> 18 19 /** 20 * This header file provides macros which help you make your class implement 21 * nsIClassInfo. Implementing nsIClassInfo is particularly helpful if you have 22 * a C++ class which implements multiple interfaces and which you access from 23 * JavaScript. If that class implements nsIClassInfo, the JavaScript code 24 * won't have to call QueryInterface on instances of the class; all methods 25 * from all interfaces returned by GetInterfaces() will be available 26 * automagically. 27 * 28 * Here's all you need to do. Given a class 29 * 30 * class nsFooBar : public nsIFoo, public nsIBar { }; 31 * 32 * you should already have the following nsISupports implementation in its cpp 33 * file: 34 * 35 * NS_IMPL_ISUPPORTS(nsFooBar, nsIFoo, nsIBar). 36 * 37 * Change this to 38 * 39 * NS_IMPL_CLASSINFO(nsFooBar, nullptr, 0, NS_FOOBAR_CID) 40 * NS_IMPL_ISUPPORTS_CI(nsFooBar, nsIFoo, nsIBar) 41 * 42 * If nsFooBar is threadsafe, change the 0 above to nsIClassInfo::THREADSAFE. 43 * If it's a singleton, use nsIClassInfo::SINGLETON. The full list of flags is 44 * in nsIClassInfo.idl. 45 * 46 * The nullptr parameter is there so you can pass a function for converting 47 * from an XPCOM object to a scriptable helper. Unless you're doing 48 * specialized JS work, you can probably leave this as nullptr. 49 * 50 * This file also defines the NS_IMPL_QUERY_INTERFACE_CI macro, which you can 51 * use to replace NS_IMPL_QUERY_INTERFACE, if you use that instead of 52 * NS_IMPL_ISUPPORTS. 53 * 54 * That's it! The rest is gory details. 55 * 56 * 57 * Notice that nsFooBar didn't need to inherit from nsIClassInfo in order to 58 * "implement" it. However, after adding these macros to nsFooBar, you you can 59 * QueryInterface an instance of nsFooBar to nsIClassInfo. How can this be? 60 * 61 * The answer lies in the NS_IMPL_ISUPPORTS_CI macro. It modifies nsFooBar's 62 * QueryInterface implementation such that, if we ask to QI to nsIClassInfo, it 63 * returns a singleton object associated with the class. (That singleton is 64 * defined by NS_IMPL_CLASSINFO.) So all nsFooBar instances will return the 65 * same object when QI'ed to nsIClassInfo. (You can see this in 66 * NS_IMPL_QUERY_CLASSINFO below.) 67 * 68 * This hack breaks XPCOM's rules, since if you take an instance of nsFooBar, 69 * QI it to nsIClassInfo, and then try to QI to nsIFoo, that will fail. On the 70 * upside, implementing nsIClassInfo doesn't add a vtable pointer to instances 71 * of your class. 72 * 73 * In principal, you can also implement nsIClassInfo by inheriting from the 74 * interface. But some code expects that when it QI's an object to 75 * nsIClassInfo, it gets back a singleton which isn't attached to any 76 * particular object. If a class were to implement nsIClassInfo through 77 * inheritance, that code might QI to nsIClassInfo and keep the resulting 78 * object alive, thinking it was only keeping alive the classinfo singleton, 79 * but in fact keeping a whole instance of the class alive. See, e.g., bug 80 * 658632. 81 * 82 * Unless you specifically need to have a different nsIClassInfo instance for 83 * each instance of your class, you should probably just implement nsIClassInfo 84 * as a singleton. 85 */ 86 87 class GenericClassInfo : public nsIClassInfo { 88 public: 89 struct ClassInfoData { 90 // This function pointer uses NS_CALLBACK_ because it's always set to an 91 // NS_IMETHOD function, which uses __stdcall on Win32. 92 typedef NS_CALLBACK_(nsresult, GetInterfacesProc)(nsTArray<nsIID>& aArray); 93 GetInterfacesProc getinterfaces; 94 95 // This function pointer doesn't use NS_CALLBACK_ because it's always set to 96 // a vanilla function. 97 typedef nsresult (*GetScriptableHelperProc)(nsIXPCScriptable** aHelper); 98 GetScriptableHelperProc getscriptablehelper; 99 100 uint32_t flags; 101 nsCID cid; 102 }; 103 104 NS_DECL_ISUPPORTS_INHERITED 105 NS_DECL_NSICLASSINFO 106 GenericClassInfo(const ClassInfoData * aData)107 explicit GenericClassInfo(const ClassInfoData* aData) : mData(aData) {} 108 109 private: 110 const ClassInfoData* mData; 111 }; 112 113 #define NS_CLASSINFO_NAME(_class) g##_class##_classInfoGlobal 114 #define NS_CI_INTERFACE_GETTER_NAME(_class) _class##_GetInterfacesHelper 115 #define NS_DECL_CI_INTERFACE_GETTER(_class) \ 116 extern NS_IMETHODIMP NS_CI_INTERFACE_GETTER_NAME(_class)(nsTArray<nsIID> & \ 117 array); 118 119 #define NS_IMPL_CLASSINFO(_class, _getscriptablehelper, _flags, _cid) \ 120 NS_DECL_CI_INTERFACE_GETTER(_class) \ 121 static const GenericClassInfo::ClassInfoData k##_class##ClassInfoData = { \ 122 NS_CI_INTERFACE_GETTER_NAME(_class), \ 123 _getscriptablehelper, \ 124 _flags | nsIClassInfo::SINGLETON_CLASSINFO, \ 125 _cid, \ 126 }; \ 127 mozilla::AlignedStorage2<GenericClassInfo> k##_class##ClassInfoDataPlace; \ 128 nsIClassInfo* NS_CLASSINFO_NAME(_class) = nullptr; 129 130 #define NS_IMPL_QUERY_CLASSINFO(_class) \ 131 if (aIID.Equals(NS_GET_IID(nsIClassInfo))) { \ 132 if (!NS_CLASSINFO_NAME(_class)) \ 133 NS_CLASSINFO_NAME(_class) = new (k##_class##ClassInfoDataPlace.addr()) \ 134 GenericClassInfo(&k##_class##ClassInfoData); \ 135 foundInterface = NS_CLASSINFO_NAME(_class); \ 136 } else 137 138 #define NS_CLASSINFO_HELPER_BEGIN(_class, _c) \ 139 NS_IMETHODIMP \ 140 NS_CI_INTERFACE_GETTER_NAME(_class)(nsTArray<nsIID> & array) { \ 141 array.Clear(); \ 142 array.SetCapacity(_c); 143 144 #define NS_CLASSINFO_HELPER_ENTRY(_interface) \ 145 array.AppendElement(NS_GET_IID(_interface)); 146 147 #define NS_CLASSINFO_HELPER_END \ 148 return NS_OK; \ 149 } 150 151 #define NS_IMPL_CI_INTERFACE_GETTER(aClass, ...) \ 152 static_assert(MOZ_ARG_COUNT(__VA_ARGS__) > 0, \ 153 "Need more arguments to NS_IMPL_CI_INTERFACE_GETTER"); \ 154 NS_CLASSINFO_HELPER_BEGIN(aClass, MOZ_ARG_COUNT(__VA_ARGS__)) \ 155 MOZ_FOR_EACH(NS_CLASSINFO_HELPER_ENTRY, (), (__VA_ARGS__)) \ 156 NS_CLASSINFO_HELPER_END 157 158 #define NS_IMPL_CI_INTERFACE_GETTER0(aClass) \ 159 NS_CLASSINFO_HELPER_BEGIN(aClass, 0) \ 160 NS_CLASSINFO_HELPER_END 161 162 // Note that this macro is an internal implementation of this header and 163 // should not be used outside it. It does not end the interface map as this 164 // is done in NS_IMPL_QUERY_INTERFACE_CI or the _INHERITED variant. 165 #define NS_IMPL_QUERY_INTERFACE_CI_GUTS(aClass, ...) \ 166 static_assert(MOZ_ARG_COUNT(__VA_ARGS__) > 0, \ 167 "Need more arguments to NS_IMPL_QUERY_INTERFACE_CI"); \ 168 NS_INTERFACE_MAP_BEGIN(aClass) \ 169 MOZ_FOR_EACH(NS_INTERFACE_MAP_ENTRY, (), (__VA_ARGS__)) \ 170 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, MOZ_ARG_1(__VA_ARGS__)) \ 171 NS_IMPL_QUERY_CLASSINFO(aClass) 172 173 #define NS_IMPL_QUERY_INTERFACE_CI(aClass, ...) \ 174 NS_IMPL_QUERY_INTERFACE_CI_GUTS(aClass, __VA_ARGS__) \ 175 NS_INTERFACE_MAP_END 176 177 #define NS_IMPL_QUERY_INTERFACE_CI_INHERITED(aClass, aSuper, ...) \ 178 NS_IMPL_QUERY_INTERFACE_CI_GUTS(aClass, __VA_ARGS__) \ 179 NS_INTERFACE_MAP_END_INHERITING \ 180 (aSuper) 181 182 #define NS_IMPL_QUERY_INTERFACE_CI_INHERITED0(aClass, aSuper) \ 183 NS_INTERFACE_MAP_BEGIN(aClass) \ 184 NS_IMPL_QUERY_CLASSINFO(aClass) \ 185 NS_INTERFACE_MAP_END_INHERITING(aSuper) 186 187 #define NS_IMPL_ISUPPORTS_CI(aClass, ...) \ 188 NS_IMPL_ADDREF(aClass) \ 189 NS_IMPL_RELEASE(aClass) \ 190 NS_IMPL_QUERY_INTERFACE_CI(aClass, __VA_ARGS__) \ 191 NS_IMPL_CI_INTERFACE_GETTER(aClass, __VA_ARGS__) 192 193 #endif // nsIClassInfoImpl_h__ 194