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