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