Line data Source code
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 : {
89 : public:
90 : struct ClassInfoData
91 : {
92 : // This function pointer uses NS_CALLBACK_ because it's always set to an
93 : // NS_IMETHOD function, which uses __stdcall on Win32.
94 : typedef NS_CALLBACK_(nsresult, GetInterfacesProc)(uint32_t* aCountP,
95 : nsIID*** aArray);
96 : GetInterfacesProc getinterfaces;
97 :
98 : // This function pointer doesn't use NS_CALLBACK_ because it's always set to
99 : // a vanilla function.
100 : typedef nsresult (*GetScriptableHelperProc)(nsIXPCScriptable** aHelper);
101 : GetScriptableHelperProc getscriptablehelper;
102 :
103 : uint32_t flags;
104 : nsCID cid;
105 : };
106 :
107 : NS_DECL_ISUPPORTS_INHERITED
108 : NS_DECL_NSICLASSINFO
109 :
110 22 : explicit GenericClassInfo(const ClassInfoData* aData) : mData(aData) {}
111 :
112 : private:
113 : const ClassInfoData* mData;
114 : };
115 :
116 : #define NS_CLASSINFO_NAME(_class) g##_class##_classInfoGlobal
117 : #define NS_CI_INTERFACE_GETTER_NAME(_class) _class##_GetInterfacesHelper
118 : #define NS_DECL_CI_INTERFACE_GETTER(_class) \
119 : extern NS_IMETHODIMP NS_CI_INTERFACE_GETTER_NAME(_class) \
120 : (uint32_t *, nsIID ***);
121 :
122 : #define NS_IMPL_CLASSINFO(_class, _getscriptablehelper, _flags, _cid) \
123 : NS_DECL_CI_INTERFACE_GETTER(_class) \
124 : static const GenericClassInfo::ClassInfoData k##_class##ClassInfoData = { \
125 : NS_CI_INTERFACE_GETTER_NAME(_class), \
126 : _getscriptablehelper, \
127 : _flags | nsIClassInfo::SINGLETON_CLASSINFO, \
128 : _cid, \
129 : }; \
130 : mozilla::AlignedStorage2<GenericClassInfo> k##_class##ClassInfoDataPlace; \
131 : nsIClassInfo* NS_CLASSINFO_NAME(_class) = nullptr;
132 :
133 : #define NS_IMPL_QUERY_CLASSINFO(_class) \
134 : if ( aIID.Equals(NS_GET_IID(nsIClassInfo)) ) { \
135 : if (!NS_CLASSINFO_NAME(_class)) \
136 : NS_CLASSINFO_NAME(_class) = new (k##_class##ClassInfoDataPlace.addr()) \
137 : GenericClassInfo(&k##_class##ClassInfoData); \
138 : foundInterface = NS_CLASSINFO_NAME(_class); \
139 : } else
140 :
141 : #define NS_CLASSINFO_HELPER_BEGIN(_class, _c) \
142 : NS_IMETHODIMP \
143 : NS_CI_INTERFACE_GETTER_NAME(_class)(uint32_t *count, nsIID ***array) \
144 : { \
145 : *count = _c; \
146 : *array = (nsIID **)moz_xmalloc(sizeof (nsIID *) * _c); \
147 : uint32_t i = 0;
148 :
149 : #define NS_CLASSINFO_HELPER_ENTRY(_interface) \
150 : (*array)[i++] = (nsIID*)nsMemory::Clone(&NS_GET_IID(_interface), \
151 : sizeof(nsIID));
152 :
153 : #define NS_CLASSINFO_HELPER_END \
154 : MOZ_ASSERT(i == *count, "Incorrent number of entries"); \
155 : return NS_OK; \
156 : }
157 :
158 : #define NS_IMPL_CI_INTERFACE_GETTER(aClass, ...) \
159 : static_assert(MOZ_ARG_COUNT(__VA_ARGS__) > 0, \
160 : "Need more arguments to NS_IMPL_CI_INTERFACE_GETTER"); \
161 : NS_CLASSINFO_HELPER_BEGIN(aClass, MOZ_ARG_COUNT(__VA_ARGS__)) \
162 : MOZ_FOR_EACH(NS_CLASSINFO_HELPER_ENTRY, (), (__VA_ARGS__)) \
163 : NS_CLASSINFO_HELPER_END
164 :
165 : #define NS_IMPL_QUERY_INTERFACE_CI(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 : NS_INTERFACE_MAP_END
173 :
174 : #define NS_IMPL_ISUPPORTS_CI(aClass, ...) \
175 : NS_IMPL_ADDREF(aClass) \
176 : NS_IMPL_RELEASE(aClass) \
177 : NS_IMPL_QUERY_INTERFACE_CI(aClass, __VA_ARGS__) \
178 : NS_IMPL_CI_INTERFACE_GETTER(aClass, __VA_ARGS__)
179 :
180 : #endif // nsIClassInfoImpl_h__
|