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 mozilla_JSEventHandler_h_
8 #define mozilla_JSEventHandler_h_
9 
10 #include "mozilla/Attributes.h"
11 #include "mozilla/MemoryReporting.h"
12 #include "mozilla/dom/EventHandlerBinding.h"
13 #include "nsCOMPtr.h"
14 #include "nsCycleCollectionParticipant.h"
15 #include "nsIAtom.h"
16 #include "nsIDOMKeyEvent.h"
17 #include "nsIDOMEventListener.h"
18 #include "nsIScriptContext.h"
19 
20 namespace mozilla {
21 
22 class TypedEventHandler
23 {
24 public:
25   enum HandlerType
26   {
27     eUnset = 0,
28     eNormal = 0x1,
29     eOnError = 0x2,
30     eOnBeforeUnload = 0x3,
31     eTypeBits = 0x3
32   };
33 
TypedEventHandler()34   TypedEventHandler()
35     : mBits(0)
36   {
37   }
38 
TypedEventHandler(dom::EventHandlerNonNull * aHandler)39   explicit TypedEventHandler(dom::EventHandlerNonNull* aHandler)
40     : mBits(0)
41   {
42     Assign(aHandler, eNormal);
43   }
44 
TypedEventHandler(dom::OnErrorEventHandlerNonNull * aHandler)45   explicit TypedEventHandler(dom::OnErrorEventHandlerNonNull* aHandler)
46     : mBits(0)
47   {
48     Assign(aHandler, eOnError);
49   }
50 
TypedEventHandler(dom::OnBeforeUnloadEventHandlerNonNull * aHandler)51   explicit TypedEventHandler(dom::OnBeforeUnloadEventHandlerNonNull* aHandler)
52     : mBits(0)
53   {
54     Assign(aHandler, eOnBeforeUnload);
55   }
56 
TypedEventHandler(const TypedEventHandler & aOther)57   TypedEventHandler(const TypedEventHandler& aOther)
58   {
59     if (aOther.HasEventHandler()) {
60       // Have to make sure we take our own ref
61       Assign(aOther.Ptr(), aOther.Type());
62     } else {
63       mBits = 0;
64     }
65   }
66 
~TypedEventHandler()67   ~TypedEventHandler()
68   {
69     ReleaseHandler();
70   }
71 
Type()72   HandlerType Type() const
73   {
74     return HandlerType(mBits & eTypeBits);
75   }
76 
HasEventHandler()77   bool HasEventHandler() const
78   {
79     return !!Ptr();
80   }
81 
SetHandler(const TypedEventHandler & aHandler)82   void SetHandler(const TypedEventHandler& aHandler)
83   {
84     if (aHandler.HasEventHandler()) {
85       ReleaseHandler();
86       Assign(aHandler.Ptr(), aHandler.Type());
87     } else {
88       ForgetHandler();
89     }
90   }
91 
NormalEventHandler()92   dom::EventHandlerNonNull* NormalEventHandler() const
93   {
94     MOZ_ASSERT(Type() == eNormal && Ptr());
95     return reinterpret_cast<dom::EventHandlerNonNull*>(Ptr());
96   }
97 
SetHandler(dom::EventHandlerNonNull * aHandler)98   void SetHandler(dom::EventHandlerNonNull* aHandler)
99   {
100     ReleaseHandler();
101     Assign(aHandler, eNormal);
102   }
103 
OnBeforeUnloadEventHandler()104   dom::OnBeforeUnloadEventHandlerNonNull* OnBeforeUnloadEventHandler() const
105   {
106     MOZ_ASSERT(Type() == eOnBeforeUnload);
107     return reinterpret_cast<dom::OnBeforeUnloadEventHandlerNonNull*>(Ptr());
108   }
109 
SetHandler(dom::OnBeforeUnloadEventHandlerNonNull * aHandler)110   void SetHandler(dom::OnBeforeUnloadEventHandlerNonNull* aHandler)
111   {
112     ReleaseHandler();
113     Assign(aHandler, eOnBeforeUnload);
114   }
115 
OnErrorEventHandler()116   dom::OnErrorEventHandlerNonNull* OnErrorEventHandler() const
117   {
118     MOZ_ASSERT(Type() == eOnError);
119     return reinterpret_cast<dom::OnErrorEventHandlerNonNull*>(Ptr());
120   }
121 
SetHandler(dom::OnErrorEventHandlerNonNull * aHandler)122   void SetHandler(dom::OnErrorEventHandlerNonNull* aHandler)
123   {
124     ReleaseHandler();
125     Assign(aHandler, eOnError);
126   }
127 
Ptr()128   dom::CallbackFunction* Ptr() const
129   {
130     // Have to cast eTypeBits so we don't have to worry about
131     // promotion issues after the bitflip.
132     return reinterpret_cast<dom::CallbackFunction*>(mBits &
133                                                       ~uintptr_t(eTypeBits));
134   }
135 
ForgetHandler()136   void ForgetHandler()
137   {
138     ReleaseHandler();
139     mBits = 0;
140   }
141 
142   bool operator==(const TypedEventHandler& aOther) const
143   {
144     return
145       Ptr() && aOther.Ptr() &&
146       Ptr()->CallbackPreserveColor() == aOther.Ptr()->CallbackPreserveColor();
147   }
148 
149 private:
150   void operator=(const TypedEventHandler&) = delete;
151 
ReleaseHandler()152   void ReleaseHandler()
153   {
154     nsISupports* ptr = Ptr();
155     NS_IF_RELEASE(ptr);
156   }
157 
Assign(nsISupports * aHandler,HandlerType aType)158   void Assign(nsISupports* aHandler, HandlerType aType)
159   {
160     MOZ_ASSERT(aHandler, "Must have handler");
161     NS_ADDREF(aHandler);
162     mBits = uintptr_t(aHandler) | uintptr_t(aType);
163   }
164 
165   uintptr_t mBits;
166 };
167 
168 /**
169  * Implemented by script event listeners. Used to retrieve the script object
170  * corresponding to the event target and the handler itself.
171  *
172  * Note, mTarget is a raw pointer and the owner of the JSEventHandler object
173  * is expected to call Disconnect()!
174  */
175 
176 #define NS_JSEVENTHANDLER_IID \
177 { 0x4f486881, 0x1956, 0x4079, \
178   { 0x8c, 0xa0, 0xf3, 0xbd, 0x60, 0x5c, 0xc2, 0x79 } }
179 
180 class JSEventHandler : public nsIDOMEventListener
181 {
182 public:
183   NS_DECLARE_STATIC_IID_ACCESSOR(NS_JSEVENTHANDLER_IID)
184 
185   JSEventHandler(nsISupports* aTarget, nsIAtom* aType,
186                  const TypedEventHandler& aTypedHandler);
187 
188   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
189 
190   // nsIDOMEventListener interface
191   NS_DECL_NSIDOMEVENTLISTENER
192 
GetEventTarget()193   nsISupports* GetEventTarget() const
194   {
195     return mTarget;
196   }
197 
Disconnect()198   void Disconnect()
199   {
200     mTarget = nullptr;
201   }
202 
GetTypedEventHandler()203   const TypedEventHandler& GetTypedEventHandler() const
204   {
205     return mTypedHandler;
206   }
207 
ForgetHandler()208   void ForgetHandler()
209   {
210     mTypedHandler.ForgetHandler();
211   }
212 
EventName()213   nsIAtom* EventName() const
214   {
215     return mEventName;
216   }
217 
218   // Set a handler for this event listener.  The handler must already
219   // be bound to the right target.
SetHandler(const TypedEventHandler & aTypedHandler)220   void SetHandler(const TypedEventHandler& aTypedHandler)
221   {
222     mTypedHandler.SetHandler(aTypedHandler);
223   }
SetHandler(dom::EventHandlerNonNull * aHandler)224   void SetHandler(dom::EventHandlerNonNull* aHandler)
225   {
226     mTypedHandler.SetHandler(aHandler);
227   }
SetHandler(dom::OnBeforeUnloadEventHandlerNonNull * aHandler)228   void SetHandler(dom::OnBeforeUnloadEventHandlerNonNull* aHandler)
229   {
230     mTypedHandler.SetHandler(aHandler);
231   }
SetHandler(dom::OnErrorEventHandlerNonNull * aHandler)232   void SetHandler(dom::OnErrorEventHandlerNonNull* aHandler)
233   {
234     mTypedHandler.SetHandler(aHandler);
235   }
236 
SizeOfExcludingThis(MallocSizeOf aMallocSizeOf)237   size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
238   {
239     return 0;
240 
241     // Measurement of the following members may be added later if DMD finds it
242     // is worthwhile:
243     // - mTarget
244     //
245     // The following members are not measured:
246     // - mTypedHandler: may be shared with others
247     // - mEventName: shared with others
248   }
249 
SizeOfIncludingThis(MallocSizeOf aMallocSizeOf)250   size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf)
251   {
252     return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
253   }
254 
255   NS_DECL_CYCLE_COLLECTION_SKIPPABLE_CLASS(JSEventHandler)
256 
257   bool IsBlackForCC();
258 
259 protected:
260   virtual ~JSEventHandler();
261 
262   nsISupports* mTarget;
263   nsCOMPtr<nsIAtom> mEventName;
264   TypedEventHandler mTypedHandler;
265 };
266 
267 NS_DEFINE_STATIC_IID_ACCESSOR(JSEventHandler, NS_JSEVENTHANDLER_IID)
268 
269 } // namespace mozilla
270 
271 /**
272  * Factory function.  aHandler must already be bound to aTarget.
273  * aContext is allowed to be null if aHandler is already set up.
274  */
275 nsresult NS_NewJSEventHandler(nsISupports* aTarget,
276                               nsIAtom* aType,
277                               const mozilla::TypedEventHandler& aTypedHandler,
278                               mozilla::JSEventHandler** aReturn);
279 
280 #endif // mozilla_JSEventHandler_h_
281 
282